libc: Check for readdir(2) errors in fts(3)

Previously, such errors were not distinguished from the end-of-directory
condition.

With improvements from Mahmoud Abumandour <ma.mandourr@gmail.com>.

Reviewed by:	markj
PR:		262038
MFC after:	2 weeks
This commit is contained in:
Ganael LAPLANCHE 2022-03-28 10:54:02 -04:00 committed by Mark Johnston
parent 6e13794fbe
commit 0cff70ca66
3 changed files with 87 additions and 9 deletions

View File

@ -610,6 +610,19 @@ __fts_set_clientptr_44bsd(FTS *sp, void *clientptr)
sp->fts_clientptr = clientptr;
}
static struct freebsd11_dirent *
fts_safe_readdir(DIR *dirp, int *readdir_errno)
{
struct freebsd11_dirent *ret;
errno = 0;
if (!dirp)
return (NULL);
ret = freebsd11_readdir(dirp);
*readdir_errno = errno;
return (ret);
}
/*
* This is the tricky part -- do not casually change *anything* in here. The
* idea is to build the linked list of entries that are used by fts_children
@ -634,7 +647,7 @@ fts_build(FTS *sp, int type)
DIR *dirp;
void *oldaddr;
int cderrno, descend, len, level, maxlen, nlinks, oflag, saved_errno,
nostat, doadjust, dnamlen;
nostat, doadjust, dnamlen, readdir_errno;
char *cp;
/* Set current node pointer. */
@ -738,8 +751,9 @@ fts_build(FTS *sp, int type)
/* Read the directory, attaching each entry to the `link' pointer. */
doadjust = 0;
readdir_errno = 0;
for (head = tail = NULL, nitems = 0;
dirp && (dp = freebsd11_readdir(dirp));) {
(dp = fts_safe_readdir(dirp, &readdir_errno));) {
dnamlen = dp->d_namlen;
if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
continue;
@ -839,6 +853,16 @@ mem1: saved_errno = errno;
}
++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)
(void)closedir(dirp);
@ -877,7 +901,8 @@ mem1: saved_errno = errno;
/* If didn't find anything, return NULL. */
if (!nitems) {
if (type == BREAD)
if (type == BREAD &&
cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
cur->fts_info = FTS_DP;
return (NULL);
}

View File

@ -607,6 +607,19 @@ freebsd11_fts_set_clientptr(FTS11 *sp, void *clientptr)
sp->fts_clientptr = clientptr;
}
static struct freebsd11_dirent *
fts_safe_readdir(DIR *dirp, int *readdir_errno)
{
struct freebsd11_dirent *ret;
errno = 0;
if (!dirp)
return (NULL);
ret = freebsd11_readdir(dirp);
*readdir_errno = errno;
return (ret);
}
/*
* This is the tricky part -- do not casually change *anything* in here. The
* idea is to build the linked list of entries that are used by fts_children
@ -630,7 +643,8 @@ fts_build(FTS11 *sp, int type)
DIR *dirp;
void *oldaddr;
char *cp;
int cderrno, descend, oflag, saved_errno, nostat, doadjust;
int cderrno, descend, oflag, saved_errno, nostat, doadjust,
readdir_errno;
long level;
long nlinks; /* has to be signed because -1 is a magic value */
size_t dnamlen, len, maxlen, nitems;
@ -736,8 +750,9 @@ fts_build(FTS11 *sp, int type)
/* Read the directory, attaching each entry to the `link' pointer. */
doadjust = 0;
readdir_errno = 0;
for (head = tail = NULL, nitems = 0;
dirp && (dp = freebsd11_readdir(dirp));) {
(dp = fts_safe_readdir(dirp, &readdir_errno));) {
dnamlen = dp->d_namlen;
if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
continue;
@ -823,6 +838,16 @@ mem1: saved_errno = errno;
}
++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)
(void)closedir(dirp);
@ -859,7 +884,8 @@ mem1: saved_errno = errno;
/* If didn't find anything, return NULL. */
if (!nitems) {
if (type == BREAD)
if (type == BREAD &&
cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
cur->fts_info = FTS_DP;
return (NULL);
}

View File

@ -604,6 +604,19 @@ fts_set_clientptr(FTS *sp, void *clientptr)
sp->fts_clientptr = clientptr;
}
static struct dirent *
fts_safe_readdir(DIR *dirp, int *readdir_errno)
{
struct dirent *ret;
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
* idea is to build the linked list of entries that are used by fts_children
@ -627,7 +640,8 @@ fts_build(FTS *sp, int type)
DIR *dirp;
void *oldaddr;
char *cp;
int cderrno, descend, oflag, saved_errno, nostat, doadjust;
int cderrno, descend, oflag, saved_errno, nostat, doadjust,
readdir_errno;
long level;
long nlinks; /* has to be signed because -1 is a magic value */
size_t dnamlen, len, maxlen, nitems;
@ -733,7 +747,9 @@ fts_build(FTS *sp, int type)
/* Read the directory, attaching each entry to the `link' pointer. */
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 = dp->d_namlen;
if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
continue;
@ -819,6 +835,16 @@ mem1: saved_errno = errno;
}
++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)
(void)closedir(dirp);
@ -855,7 +881,8 @@ mem1: saved_errno = errno;
/* If didn't find anything, return NULL. */
if (!nitems) {
if (type == BREAD)
if (type == BREAD &&
cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
cur->fts_info = FTS_DP;
return (NULL);
}