The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=0cff70ca66547ca5b04030ef07e6a0b9759a0184

commit 0cff70ca66547ca5b04030ef07e6a0b9759a0184
Author:     Ganael LAPLANCHE <marty...@freebsd.org>
AuthorDate: 2022-03-28 14:54:02 +0000
Commit:     Mark Johnston <ma...@freebsd.org>
CommitDate: 2022-03-28 15:23:46 +0000

    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.mando...@gmail.com>.
    
    Reviewed by:    markj
    PR:             262038
    MFC after:      2 weeks
---
 lib/libc/gen/fts-compat.c   | 31 ++++++++++++++++++++++++++++---
 lib/libc/gen/fts-compat11.c | 32 +++++++++++++++++++++++++++++---
 lib/libc/gen/fts.c          | 33 ++++++++++++++++++++++++++++++---
 3 files changed, 87 insertions(+), 9 deletions(-)

diff --git a/lib/libc/gen/fts-compat.c b/lib/libc/gen/fts-compat.c
index ccdd4f15905b..9f295110f1c7 100644
--- a/lib/libc/gen/fts-compat.c
+++ b/lib/libc/gen/fts-compat.c
@@ -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);
        }
diff --git a/lib/libc/gen/fts-compat11.c b/lib/libc/gen/fts-compat11.c
index 288351d2008b..a18ce3f00a6a 100644
--- a/lib/libc/gen/fts-compat11.c
+++ b/lib/libc/gen/fts-compat11.c
@@ -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);
        }
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c
index d0705e123775..5186ae047a3b 100644
--- a/lib/libc/gen/fts.c
+++ b/lib/libc/gen/fts.c
@@ -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);
        }

Reply via email to