fts: Roll up changes from FreeBSD.

Notable changes include:

 * fts: Stat things relative to the directory fd, if possible.
 * fts: Fix double-free with conflicting concurrent modifications.
 * fts: Don't abort if an empty pathname is given.
 * fts: Don't return FTS_SLNONE if it's not a symlink (if race).
 * fts_children: preserve errno after running close/fchdir
 * Use ANSI C prototypes. Eliminates -Wold-style-definition warnings.
 * fts: Fix a potential memory leak in error case
 * libc: Check for readdir(2) errors in fts(3)
This commit is contained in:
Augustin Cavalier
2023-07-29 12:25:37 -04:00
parent 26710c49ff
commit 4c47466fea
+107 -116
View File
@@ -1,4 +1,6 @@
/*- /*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1990, 1993, 1994 * Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
* *
@@ -10,7 +12,7 @@
* 2. Redistributions in binary form must reproduce the above copyright * 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors * 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software * may be used to endorse or promote products derived from this software
* without specific prior written permission. * without specific prior written permission.
* *
@@ -29,12 +31,6 @@
* $OpenBSD: fts.c,v 1.22 1999/10/03 19:22:22 millert Exp $ * $OpenBSD: fts.c,v 1.22 1999/10/03 19:22:22 millert Exp $
*/ */
#if 0
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
#endif /* LIBC_SCCS and not lint */
#endif
#include <sys/cdefs.h> #include <sys/cdefs.h>
#ifdef __HAIKU__ #ifdef __HAIKU__
@@ -49,6 +45,7 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#else #else
__SCCSID("@(#)fts.c 8.6 (Berkeley) 8/14/94");
__FBSDID("$FreeBSD$"); __FBSDID("$FreeBSD$");
#include "namespace.h" #include "namespace.h"
@@ -76,22 +73,10 @@ 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 *, size_t); static FTSENT *fts_sort(FTS *, FTSENT *, size_t);
static int fts_stat(FTS *, FTSENT *, int); static int fts_stat(FTS *, FTSENT *, int, int);
static int fts_safe_changedir(FTS *, FTSENT *, int, char *); static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
static int fts_ufslinks(FTS *, const FTSENT *); static int fts_ufslinks(FTS *, const FTSENT *);
FTS * __fts_open(char * const *argv, int options, int (*compar)(
const FTSENT * const *, const FTSENT * const *));
int __fts_close(FTS *sp);
FTSENT * __fts_read(FTS *sp);
int __fts_set(FTS *sp, FTSENT *p, int instr);
FTSENT * __fts_children(FTS *sp, int instr);
void *(__fts_get_clientptr)(FTS *sp);
FTS * (__fts_get_stream)(FTSENT *p);
void __fts_set_clientptr(FTS *sp, void *clientptr);
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
#define CLR(opt) (sp->fts_options &= ~(opt)) #define CLR(opt) (sp->fts_options &= ~(opt))
@@ -133,17 +118,28 @@ static const char *ufslike_filesystems[] = {
"ufs", "ufs",
"zfs", "zfs",
"nfs", "nfs",
"nfs4",
"ext2fs", "ext2fs",
0 0
}; };
#endif /* !__HAIKU__ */ #endif /* !__HAIKU__ */
#ifdef __HAIKU__
static void *
reallocf(void *ptr, size_t size)
{
void *nptr;
nptr = realloc(ptr, size);
if (!nptr)
free(ptr);
return (nptr);
}
#endif
FTS * FTS *
__fts_open(argv, options, compar) fts_open(char * const *argv, int options,
char * const *argv; int (*compar)(const FTSENT * const *, const FTSENT * const *))
int options;
int (*compar)(const FTSENT * const *, const FTSENT * const *);
{ {
struct _fts_private *priv; struct _fts_private *priv;
FTS *sp; FTS *sp;
@@ -170,9 +166,6 @@ __fts_open(argv, options, compar)
sp->fts_compar = compar; sp->fts_compar = compar;
sp->fts_options = options; sp->fts_options = options;
/* Shush, GCC. */
tmp = NULL;
/* Logical walks turn on NOCHDIR; symbolic links are too hard. */ /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
if (ISSET(FTS_LOGICAL)) if (ISSET(FTS_LOGICAL))
SET(FTS_NOCHDIR); SET(FTS_NOCHDIR);
@@ -189,19 +182,18 @@ __fts_open(argv, options, compar)
goto mem2; goto mem2;
parent->fts_level = FTS_ROOTPARENTLEVEL; parent->fts_level = FTS_ROOTPARENTLEVEL;
/* Shush, GCC. */
tmp = NULL;
/* Allocate/initialize root(s). */ /* Allocate/initialize root(s). */
for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) { for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
/* Don't allow zero-length paths. */ len = strlen(*argv);
if ((len = strlen(*argv)) == 0) {
errno = ENOENT;
goto mem3;
}
p = fts_alloc(sp, *argv, len); p = fts_alloc(sp, *argv, len);
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;
p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1);
/* Command-line "." and ".." are real directories. */ /* Command-line "." and ".." are real directories. */
if (p->fts_info == FTS_DOT) if (p->fts_info == FTS_DOT)
@@ -257,10 +249,6 @@ mem1: free(sp);
return (NULL); return (NULL);
} }
__weak_reference(__fts_open, fts_open);
static void static void
fts_load(FTS *sp, FTSENT *p) fts_load(FTS *sp, FTSENT *p)
{ {
@@ -286,7 +274,7 @@ fts_load(FTS *sp, FTSENT *p)
} }
int int
__fts_close(FTS *sp) fts_close(FTS *sp)
{ {
FTSENT *freep, *p; FTSENT *freep, *p;
int saved_errno; int saved_errno;
@@ -331,10 +319,6 @@ __fts_close(FTS *sp)
return (0); return (0);
} }
__weak_reference(__fts_close, fts_close);
/* /*
* Special case of "/" at the end of the path so that slashes aren't * Special case of "/" at the end of the path so that slashes aren't
* appended which would cause paths to be written as "....//foo". * appended which would cause paths to be written as "....//foo".
@@ -344,7 +328,7 @@ __weak_reference(__fts_close, fts_close);
? p->fts_pathlen - 1 : p->fts_pathlen) ? p->fts_pathlen - 1 : p->fts_pathlen)
FTSENT * FTSENT *
__fts_read(FTS *sp) fts_read(FTS *sp)
{ {
FTSENT *p, *tmp; FTSENT *p, *tmp;
int instr; int instr;
@@ -364,7 +348,7 @@ __fts_read(FTS *sp)
/* Any type of file may be re-visited; re-stat and re-turn. */ /* Any type of file may be re-visited; re-stat and re-turn. */
if (instr == FTS_AGAIN) { if (instr == FTS_AGAIN) {
p->fts_info = fts_stat(sp, p, 0); p->fts_info = fts_stat(sp, p, 0, -1);
return (p); return (p);
} }
@@ -376,7 +360,7 @@ __fts_read(FTS *sp)
*/ */
if (instr == FTS_FOLLOW && if (instr == FTS_FOLLOW &&
(p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
p->fts_info = fts_stat(sp, p, 1); p->fts_info = fts_stat(sp, p, 1, -1);
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
if ((p->fts_symfd = open(".", O_RDONLY | O_CLOEXEC, if ((p->fts_symfd = open(".", O_RDONLY | O_CLOEXEC,
0)) < 0) { 0)) < 0) {
@@ -444,8 +428,6 @@ __fts_read(FTS *sp)
/* Move to the next node on this level. */ /* Move to the next node on this level. */
next: tmp = p; next: tmp = p;
if ((p = p->fts_link) != NULL) { if ((p = p->fts_link) != NULL) {
free(tmp);
/* /*
* If reached the top, return to the original directory (or * If reached the top, return to the original directory (or
* 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.
@@ -455,6 +437,7 @@ next: tmp = p;
SET(FTS_STOP); SET(FTS_STOP);
return (NULL); return (NULL);
} }
free(tmp);
fts_load(sp, p); fts_load(sp, p);
return (sp->fts_cur = p); return (sp->fts_cur = p);
} }
@@ -464,10 +447,12 @@ next: tmp = p;
* ignore. If followed, get a file descriptor so we can * ignore. If followed, get a file descriptor so we can
* get back if necessary. * get back if necessary.
*/ */
if (p->fts_instr == FTS_SKIP) if (p->fts_instr == FTS_SKIP) {
free(tmp);
goto next; goto next;
}
if (p->fts_instr == FTS_FOLLOW) { if (p->fts_instr == FTS_FOLLOW) {
p->fts_info = fts_stat(sp, p, 1); p->fts_info = fts_stat(sp, p, 1, -1);
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
if ((p->fts_symfd = if ((p->fts_symfd =
open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) { open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
@@ -479,6 +464,8 @@ next: tmp = p;
p->fts_instr = FTS_NOINSTR; p->fts_instr = FTS_NOINSTR;
} }
free(tmp);
name: t = sp->fts_path + NAPPEND(p->fts_parent); name: t = sp->fts_path + NAPPEND(p->fts_parent);
*t++ = '/'; *t++ = '/';
memmove(t, p->fts_name, p->fts_namelen + 1); memmove(t, p->fts_name, p->fts_namelen + 1);
@@ -487,13 +474,13 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent);
/* Move up to the parent node. */ /* Move up to the parent node. */
p = tmp->fts_parent; p = tmp->fts_parent;
free(tmp);
if (p->fts_level == FTS_ROOTPARENTLEVEL) { if (p->fts_level == FTS_ROOTPARENTLEVEL) {
/* /*
* Done; free everything up and set errno to 0 so the user * Done; free everything up and set errno to 0 so the user
* can distinguish between error and EOF. * can distinguish between error and EOF.
*/ */
free(tmp);
free(p); free(p);
errno = 0; errno = 0;
return (sp->fts_cur = NULL); return (sp->fts_cur = NULL);
@@ -526,14 +513,11 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent);
SET(FTS_STOP); SET(FTS_STOP);
return (NULL); return (NULL);
} }
free(tmp);
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);
} }
__weak_reference(__fts_read, fts_read);
/* /*
* Fts_set takes the stream as an argument although it's not used in this * Fts_set takes the stream as an argument although it's not used in this
* implementation; it would be necessary if anyone wanted to add global * implementation; it would be necessary if anyone wanted to add global
@@ -542,7 +526,7 @@ __weak_reference(__fts_read, fts_read);
*/ */
/* ARGSUSED */ /* ARGSUSED */
int int
__fts_set(FTS *sp, FTSENT *p, int instr) fts_set(FTS *sp, FTSENT *p, int instr)
{ {
if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW && if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
instr != FTS_NOINSTR && instr != FTS_SKIP) { instr != FTS_NOINSTR && instr != FTS_SKIP) {
@@ -553,15 +537,11 @@ __fts_set(FTS *sp, FTSENT *p, int instr)
return (0); return (0);
} }
__weak_reference(__fts_set, fts_set);
FTSENT * FTSENT *
__fts_children(FTS *sp, int instr) fts_children(FTS *sp, int instr)
{ {
FTSENT *p; FTSENT *p;
int fd; int fd, rc, serrno;
if (instr != 0 && instr != FTS_NAMEONLY) { if (instr != 0 && instr != FTS_NAMEONLY) {
errno = EINVAL; errno = EINVAL;
@@ -617,57 +597,57 @@ __fts_children(FTS *sp, int instr)
if ((fd = open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) if ((fd = open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
return (NULL); return (NULL);
sp->fts_child = fts_build(sp, instr); sp->fts_child = fts_build(sp, instr);
if (fchdir(fd)) { serrno = (sp->fts_child == NULL) ? errno : 0;
rc = fchdir(fd);
if (rc < 0 && serrno == 0)
serrno = errno;
(void)close(fd); (void)close(fd);
errno = serrno;
if (rc < 0)
return (NULL); return (NULL);
}
(void)close(fd);
return (sp->fts_child); return (sp->fts_child);
} }
__weak_reference(__fts_children, fts_children);
#ifndef fts_get_clientptr #ifndef fts_get_clientptr
#error "fts_get_clientptr not defined" #error "fts_get_clientptr not defined"
#endif #endif
void * void *
(__fts_get_clientptr)(FTS *sp) (fts_get_clientptr)(FTS *sp)
{ {
return (fts_get_clientptr(sp)); return (fts_get_clientptr(sp));
} }
__weak_reference(__fts_get_clientptr, fts_get_clientptr);
#ifndef fts_get_stream #ifndef fts_get_stream
#error "fts_get_stream not defined" #error "fts_get_stream not defined"
#endif #endif
FTS * FTS *
(__fts_get_stream)(FTSENT *p) (fts_get_stream)(FTSENT *p)
{ {
return (fts_get_stream(p)); return (fts_get_stream(p));
} }
__weak_reference(__fts_get_stream, fts_get_stream);
void void
__fts_set_clientptr(FTS *sp, void *clientptr) fts_set_clientptr(FTS *sp, void *clientptr)
{ {
sp->fts_clientptr = clientptr; sp->fts_clientptr = clientptr;
} }
static struct dirent *
fts_safe_readdir(DIR *dirp, int *readdir_errno)
{
struct dirent *ret;
__weak_reference(__fts_set_clientptr, fts_set_clientptr); errno = 0;
if (!dirp)
return (NULL);
ret = readdir(dirp);
*readdir_errno = errno;
return (ret);
}
/* /*
* This is the tricky part -- do not casually change *anything* in here. The * This is the tricky part -- do not casually change *anything* in here. The
@@ -692,7 +672,8 @@ fts_build(FTS *sp, int type)
DIR *dirp; DIR *dirp;
void *oldaddr; void *oldaddr;
char *cp; char *cp;
int cderrno, descend, saved_errno, nostat, doadjust; int cderrno, descend, saved_errno, nostat, doadjust,
readdir_errno;
#ifdef FTS_WHITEOUT #ifdef FTS_WHITEOUT
int oflag; int oflag;
#endif #endif
@@ -709,9 +690,9 @@ fts_build(FTS *sp, int type)
*/ */
#ifdef FTS_WHITEOUT #ifdef FTS_WHITEOUT
if (ISSET(FTS_WHITEOUT)) if (ISSET(FTS_WHITEOUT))
oflag = DTF_NODUP | DTF_REWIND; oflag = DTF_NODUP;
else else
oflag = DTF_HIDEW | DTF_NODUP | DTF_REWIND; oflag = DTF_HIDEW | DTF_NODUP;
#else #else
#define __opendir2(path, flag) opendir(path) #define __opendir2(path, flag) opendir(path)
#endif #endif
@@ -801,7 +782,9 @@ fts_build(FTS *sp, int type)
/* Read the directory, attaching each entry to the `link' pointer. */ /* Read the directory, attaching each entry to the `link' pointer. */
doadjust = 0; doadjust = 0;
for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) { readdir_errno = 0;
for (head = tail = NULL, nitems = 0;
(dp = fts_safe_readdir(dirp, &readdir_errno));) {
dnamlen = strlen(dp->d_name); dnamlen = strlen(dp->d_name);
if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
continue; continue;
@@ -865,10 +848,11 @@ mem1: saved_errno = errno;
if (ISSET(FTS_NOCHDIR)) { if (ISSET(FTS_NOCHDIR)) {
p->fts_accpath = p->fts_path; p->fts_accpath = p->fts_path;
memmove(cp, p->fts_name, p->fts_namelen + 1); memmove(cp, p->fts_name, p->fts_namelen + 1);
} else p->fts_info = fts_stat(sp, p, 0, dirfd(dirp));
} else {
p->fts_accpath = p->fts_name; p->fts_accpath = p->fts_name;
/* Stat it. */ p->fts_info = fts_stat(sp, p, 0, -1);
p->fts_info = fts_stat(sp, p, 0); }
/* Decrement link count if applicable. */ /* Decrement link count if applicable. */
if (nlinks > 0 && (p->fts_info == FTS_D || if (nlinks > 0 && (p->fts_info == FTS_D ||
@@ -886,6 +870,16 @@ mem1: saved_errno = errno;
} }
++nitems; ++nitems;
} }
if (readdir_errno) {
cur->fts_errno = readdir_errno;
/*
* If we've not read any items yet, treat
* the error as if we can't access the dir.
*/
cur->fts_info = nitems ? FTS_ERR : FTS_DNR;
}
if (dirp) if (dirp)
(void)closedir(dirp); (void)closedir(dirp);
@@ -914,6 +908,7 @@ mem1: saved_errno = errno;
(cur->fts_level == FTS_ROOTLEVEL ? (cur->fts_level == FTS_ROOTLEVEL ?
FCHDIR(sp, sp->fts_rfd) : FCHDIR(sp, sp->fts_rfd) :
fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
fts_lfree(head);
cur->fts_info = FTS_ERR; cur->fts_info = FTS_ERR;
SET(FTS_STOP); SET(FTS_STOP);
return (NULL); return (NULL);
@@ -921,7 +916,8 @@ mem1: saved_errno = errno;
/* If didn't find anything, return NULL. */ /* If didn't find anything, return NULL. */
if (!nitems) { if (!nitems) {
if (type == BREAD) if (type == BREAD &&
cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
cur->fts_info = FTS_DP; cur->fts_info = FTS_DP;
return (NULL); return (NULL);
} }
@@ -933,13 +929,19 @@ mem1: saved_errno = errno;
} }
static int static int
fts_stat(FTS *sp, FTSENT *p, int follow) fts_stat(FTS *sp, FTSENT *p, int follow, int dfd)
{ {
FTSENT *t; FTSENT *t;
dev_t dev; dev_t dev;
ino_t ino; ino_t ino;
struct stat *sbp, sb; struct stat *sbp, sb;
int saved_errno; int saved_errno;
const char *path;
if (dfd == -1)
path = p->fts_accpath, dfd = AT_FDCWD;
else
path = p->fts_name;
/* If user needs stat info, stat buffer already allocated. */ /* If user needs stat info, stat buffer already allocated. */
sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
@@ -961,16 +963,17 @@ fts_stat(FTS *sp, FTSENT *p, int follow)
* fail, set the errno from the stat call. * fail, set the errno from the stat call.
*/ */
if (ISSET(FTS_LOGICAL) || follow) { if (ISSET(FTS_LOGICAL) || follow) {
if (stat(p->fts_accpath, sbp)) { if (fstatat(dfd, path, sbp, 0)) {
saved_errno = errno; saved_errno = errno;
if (!lstat(p->fts_accpath, sbp)) { if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
errno = 0;
return (FTS_SLNONE);
}
p->fts_errno = saved_errno; p->fts_errno = saved_errno;
goto err; goto err;
} }
} else if (lstat(p->fts_accpath, sbp)) { errno = 0;
if (S_ISLNK(sbp->st_mode))
return (FTS_SLNONE);
}
} else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
p->fts_errno = errno; p->fts_errno = errno;
err: memset(sbp, 0, sizeof(struct stat)); err: memset(sbp, 0, sizeof(struct stat));
return (FTS_NS); return (FTS_NS);
@@ -1031,7 +1034,6 @@ static FTSENT *
fts_sort(FTS *sp, FTSENT *head, size_t nitems) fts_sort(FTS *sp, FTSENT *head, size_t nitems)
{ {
FTSENT **ap, *p; FTSENT **ap, *p;
FTSENT **new_array;
/* /*
* Construct an array of pointers to the structures and call qsort(3). * Construct an array of pointers to the structures and call qsort(3).
@@ -1042,14 +1044,11 @@ fts_sort(FTS *sp, FTSENT *head, size_t nitems)
*/ */
if (nitems > sp->fts_nitems) { if (nitems > sp->fts_nitems) {
sp->fts_nitems = nitems + 40; sp->fts_nitems = nitems + 40;
new_array = realloc(sp->fts_array, sp->fts_nitems * sizeof(FTSENT *)); if ((sp->fts_array = reallocf(sp->fts_array,
if (new_array == NULL) { sp->fts_nitems * sizeof(FTSENT *))) == NULL) {
free(sp->fts_array);
sp->fts_array = NULL;
sp->fts_nitems = 0; sp->fts_nitems = 0;
return (head); return (head);
} }
sp->fts_array = new_array;
} }
for (ap = sp->fts_array, p = head; p; p = p->fts_link) for (ap = sp->fts_array, p = head; p; p = p->fts_link)
*ap++ = p; *ap++ = p;
@@ -1128,17 +1127,9 @@ fts_lfree(FTSENT *head)
static int static int
fts_palloc(FTS *sp, size_t more) fts_palloc(FTS *sp, size_t more)
{ {
char *new_path;
sp->fts_pathlen += more + 256; sp->fts_pathlen += more + 256;
sp->fts_path = reallocf(sp->fts_path, sp->fts_pathlen);
new_path = realloc(sp->fts_path, sp->fts_pathlen);
if (new_path == NULL) {
free(sp->fts_path);
sp->fts_path = NULL;
} else {
sp->fts_path = new_path;
}
return (sp->fts_path == NULL); return (sp->fts_path == NULL);
} }
@@ -1171,8 +1162,7 @@ fts_padjust(FTS *sp, FTSENT *head)
} }
static size_t static size_t
fts_maxarglen(argv) fts_maxarglen(char * const *argv)
char * const *argv;
{ {
size_t len, max; size_t len, max;
@@ -1196,7 +1186,8 @@ fts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path)
newfd = fd; newfd = fd;
if (ISSET(FTS_NOCHDIR)) if (ISSET(FTS_NOCHDIR))
return (0); return (0);
if (fd < 0 && (newfd = open(path, O_RDONLY | O_CLOEXEC, 0)) < 0) if (fd < 0 && (newfd = open(path, O_RDONLY | O_DIRECTORY |
O_CLOEXEC, 0)) < 0)
return (-1); return (-1);
if (fstat(newfd, &sb)) { if (fstat(newfd, &sb)) {
ret = -1; ret = -1;