Merge "Fix various ftw/nftw "shall fail"s from POSIX."
This commit is contained in:
commit
68e1c5b752
4 changed files with 83 additions and 40 deletions
|
@ -70,22 +70,13 @@ void* reallocarray(void*, size_t, size_t);
|
|||
#define BNAMES 2 /* fts_children, names only */
|
||||
#define BREAD 3 /* fts_read */
|
||||
|
||||
FTS *
|
||||
fts_open(char * const *argv, int options,
|
||||
int (*compar)(const FTSENT **, const FTSENT **))
|
||||
{
|
||||
FTS* __fts_open(char* const* argv, int options, int (*compar)(const FTSENT**, const FTSENT**)) {
|
||||
FTS *sp;
|
||||
FTSENT *p, *root;
|
||||
int nitems;
|
||||
FTSENT *parent, *tmp;
|
||||
size_t len;
|
||||
|
||||
/* Options check. */
|
||||
if (options & ~FTS_OPTIONMASK) {
|
||||
errno = EINVAL;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Allocate/initialize the stream */
|
||||
if ((sp = calloc(1, sizeof(FTS))) == NULL)
|
||||
return (NULL);
|
||||
|
@ -123,6 +114,9 @@ fts_open(char * const *argv, int options,
|
|||
p->fts_accpath = p->fts_name;
|
||||
p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1);
|
||||
|
||||
// For ftw/nftw we need to fail early: http://b/31152735
|
||||
if ((options & FTS_FOR_FTW) != 0 && p->fts_info == FTS_NS) goto mem3;
|
||||
|
||||
/* Command-line "." and ".." are real directories. */
|
||||
if (p->fts_info == FTS_DOT)
|
||||
p->fts_info = FTS_D;
|
||||
|
@ -164,7 +158,7 @@ fts_open(char * const *argv, int options,
|
|||
* 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)
|
||||
if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0)
|
||||
SET(FTS_NOCHDIR);
|
||||
|
||||
if (nitems == 0)
|
||||
|
@ -227,10 +221,8 @@ fts_close(FTS *sp)
|
|||
rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd;
|
||||
|
||||
/* Free up child linked list, sort array, path buffer, stream ptr.*/
|
||||
if (sp->fts_child)
|
||||
fts_lfree(sp->fts_child);
|
||||
if (sp->fts_array)
|
||||
free(sp->fts_array);
|
||||
fts_lfree(sp->fts_child);
|
||||
free(sp->fts_array);
|
||||
free(sp->fts_path);
|
||||
free(sp);
|
||||
|
||||
|
@ -289,7 +281,7 @@ fts_read(FTS *sp)
|
|||
(p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
|
||||
p->fts_info = fts_stat(sp, p, 1, -1);
|
||||
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
|
||||
if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) {
|
||||
if ((p->fts_symfd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0) {
|
||||
p->fts_errno = errno;
|
||||
p->fts_info = FTS_ERR;
|
||||
} else
|
||||
|
@ -378,8 +370,7 @@ next: tmp = p;
|
|||
if (p->fts_instr == FTS_FOLLOW) {
|
||||
p->fts_info = fts_stat(sp, p, 1, -1);
|
||||
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
|
||||
if ((p->fts_symfd =
|
||||
open(".", O_RDONLY, 0)) < 0) {
|
||||
if ((p->fts_symfd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0) {
|
||||
p->fts_errno = errno;
|
||||
p->fts_info = FTS_ERR;
|
||||
} else
|
||||
|
@ -498,8 +489,7 @@ fts_children(FTS *sp, int instr)
|
|||
return (NULL);
|
||||
|
||||
/* Free up any previous child list. */
|
||||
if (sp->fts_child)
|
||||
fts_lfree(sp->fts_child);
|
||||
fts_lfree(sp->fts_child);
|
||||
|
||||
if (instr == FTS_NAMEONLY) {
|
||||
SET(FTS_NAMEONLY);
|
||||
|
@ -518,7 +508,7 @@ fts_children(FTS *sp, int instr)
|
|||
ISSET(FTS_NOCHDIR))
|
||||
return (sp->fts_child = fts_build(sp, instr));
|
||||
|
||||
if ((fd = open(".", O_RDONLY, 0)) < 0)
|
||||
if ((fd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0)
|
||||
return (NULL);
|
||||
sp->fts_child = fts_build(sp, instr);
|
||||
if (fchdir(fd)) {
|
||||
|
@ -664,8 +654,7 @@ fts_build(FTS *sp, int type)
|
|||
* structures already allocated.
|
||||
*/
|
||||
mem1: saved_errno = errno;
|
||||
if (p)
|
||||
free(p);
|
||||
free(p);
|
||||
fts_lfree(head);
|
||||
(void)closedir(dirp);
|
||||
cur->fts_info = FTS_ERR;
|
||||
|
@ -817,9 +806,9 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd)
|
|||
* fail, set the errno from the stat call.
|
||||
*/
|
||||
if (ISSET(FTS_LOGICAL) || follow) {
|
||||
if (fstatat(dfd, path, sbp, 0)) {
|
||||
if (fstatat(dfd, path, sbp, 0) == -1) {
|
||||
saved_errno = errno;
|
||||
if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
|
||||
if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW) == 0) {
|
||||
errno = 0;
|
||||
return (FTS_SLNONE);
|
||||
}
|
||||
|
@ -886,8 +875,7 @@ fts_sort(FTS *sp, FTSENT *head, int nitems)
|
|||
sp->fts_nitems = nitems + 40;
|
||||
if ((a = reallocarray(sp->fts_array,
|
||||
sp->fts_nitems, sizeof(FTSENT *))) == NULL) {
|
||||
if (sp->fts_array)
|
||||
free(sp->fts_array);
|
||||
free(sp->fts_array);
|
||||
sp->fts_array = NULL;
|
||||
sp->fts_nitems = 0;
|
||||
return (head);
|
||||
|
@ -961,8 +949,7 @@ fts_palloc(FTS *sp, size_t more)
|
|||
*/
|
||||
more += 256;
|
||||
if (sp->fts_pathlen + more < sp->fts_pathlen) {
|
||||
if (sp->fts_path)
|
||||
free(sp->fts_path);
|
||||
free(sp->fts_path);
|
||||
sp->fts_path = NULL;
|
||||
errno = ENAMETOOLONG;
|
||||
return (1);
|
||||
|
@ -970,8 +957,7 @@ fts_palloc(FTS *sp, size_t more)
|
|||
sp->fts_pathlen += more;
|
||||
p = realloc(sp->fts_path, sp->fts_pathlen);
|
||||
if (p == NULL) {
|
||||
if (sp->fts_path)
|
||||
free(sp->fts_path);
|
||||
free(sp->fts_path);
|
||||
sp->fts_path = NULL;
|
||||
return (1);
|
||||
}
|
||||
|
@ -1032,7 +1018,7 @@ fts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path)
|
|||
newfd = fd;
|
||||
if (ISSET(FTS_NOCHDIR))
|
||||
return (0);
|
||||
if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0)
|
||||
if (fd < 0 && (newfd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0)) < 0)
|
||||
return (-1);
|
||||
if (fstat(newfd, &sb)) {
|
||||
ret = -1;
|
||||
|
@ -1051,3 +1037,12 @@ bail:
|
|||
errno = oerrno;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
FTS* fts_open(char* const* argv, int options, int (*compar)(const FTSENT**, const FTSENT**)) {
|
||||
// Options check.
|
||||
if ((options & ~FTS_OPTIONMASK) != 0) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return __fts_open(argv, options, compar);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int do_nftw(const char *path,
|
||||
extern "C" FTS* __fts_open(char* const*, int, int (*)(const FTSENT**, const FTSENT**));
|
||||
|
||||
static int do_nftw(const char* path,
|
||||
int (*ftw_fn)(const char*, const struct stat*, int),
|
||||
int (*nftw_fn)(const char*, const struct stat*, int, FTW*),
|
||||
int nfds,
|
||||
|
@ -47,7 +49,7 @@ static int do_nftw(const char *path,
|
|||
|
||||
// Call fts_open.
|
||||
char* const paths[2] = { const_cast<char*>(path), nullptr };
|
||||
FTS* fts = fts_open(paths, fts_options, nullptr);
|
||||
FTS* fts = __fts_open(paths, fts_options | FTS_FOR_FTW, nullptr);
|
||||
if (fts == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -64,6 +66,9 @@ static int do_nftw(const char *path,
|
|||
if (postorder || access(cur->fts_path, R_OK) == -1) continue;
|
||||
fn_flag = FTW_D;
|
||||
break;
|
||||
case FTS_DC:
|
||||
// POSIX says nftw "shall not report" directories causing loops (http://b/31152735).
|
||||
continue;
|
||||
case FTS_DNR:
|
||||
fn_flag = FTW_DNR;
|
||||
break;
|
||||
|
@ -85,10 +90,6 @@ static int do_nftw(const char *path,
|
|||
case FTS_SLNONE:
|
||||
fn_flag = (nftw_fn != nullptr) ? FTW_SLN : FTW_NS;
|
||||
break;
|
||||
case FTS_DC:
|
||||
errno = ELOOP;
|
||||
error = -1;
|
||||
continue;
|
||||
default:
|
||||
error = -1;
|
||||
continue;
|
||||
|
|
|
@ -58,8 +58,9 @@ typedef struct {
|
|||
#define FTS_XDEV 0x0040 /* don't cross devices */
|
||||
#define FTS_OPTIONMASK 0x00ff /* valid user option mask */
|
||||
|
||||
#define FTS_NAMEONLY 0x1000 /* (private) child names only */
|
||||
#define FTS_STOP 0x2000 /* (private) unrecoverable error */
|
||||
#define FTS_NAMEONLY 0x1000 /* (private) child names only */
|
||||
#define FTS_STOP 0x2000 /* (private) unrecoverable error */
|
||||
#define FTS_FOR_FTW 0x4000 /* (private) fts is being called by ftw/nftw */
|
||||
int fts_options; /* fts_open options, global flags */
|
||||
} FTS;
|
||||
|
||||
|
|
|
@ -153,3 +153,49 @@ TEST(ftw, bug_28197840) {
|
|||
ASSERT_EQ(0, nftw(root.dirname, bug_28197840_nftw<struct stat>, 128, FTW_PHYS));
|
||||
ASSERT_EQ(0, nftw64(root.dirname, bug_28197840_nftw<struct stat64>, 128, FTW_PHYS));
|
||||
}
|
||||
|
||||
template <typename StatT>
|
||||
static int null_ftw_callback(const char*, const StatT*, int) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename StatT>
|
||||
static int null_nftw_callback(const char*, const StatT*, int, FTW*) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(ftw, ftw_non_existent_ENOENT) {
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, ftw("/does/not/exist", null_ftw_callback<struct stat>, 128));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, ftw64("/does/not/exist", null_ftw_callback<struct stat64>, 128));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
}
|
||||
|
||||
TEST(ftw, nftw_non_existent_ENOENT) {
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, nftw("/does/not/exist", null_nftw_callback<struct stat>, 128, FTW_PHYS));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, nftw64("/does/not/exist", null_nftw_callback<struct stat64>, 128, FTW_PHYS));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
}
|
||||
|
||||
TEST(ftw, ftw_empty_ENOENT) {
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, ftw("", null_ftw_callback<struct stat>, 128));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, ftw64("", null_ftw_callback<struct stat64>, 128));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
}
|
||||
|
||||
TEST(ftw, nftw_empty_ENOENT) {
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, nftw("", null_nftw_callback<struct stat>, 128, FTW_PHYS));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, nftw64("", null_nftw_callback<struct stat64>, 128, FTW_PHYS));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue