From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1791) id C53F73858416; Fri, 27 Jan 2023 20:39:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C53F73858416 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1674851989; bh=0kedBbT76/7GGBxPTmhjWJaseAMVQ1zUYsSeUMBkQP0=; h=From:To:Subject:Date:From; b=g0ZaZu5eUuBfkp5vGIfSaChZCEF2Ny3dmkaIcscE0A1Mz3Pbn2YsDSn76vDPzNuwv mBM5McK9aoD8BMoglHz80pHfPAazNRfoMEgDdwVu3+FWwCKOLb04W0HAY1NxFpsU+t kBlPL7cCWEAPFiPgawDMIx000ZOwj9BNlVIgz0vc= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Adhemerval Zanella To: glibc-cvs@sourceware.org Subject: [glibc/azanella/bz23960-dirent] linux: Use getdents64 on non-LFS readdir X-Act-Checkin: glibc X-Git-Author: Adhemerval Zanella X-Git-Refname: refs/heads/azanella/bz23960-dirent X-Git-Oldrev: 0d50f477f47ba637b54fb03ac48d769ec4543e8d X-Git-Newrev: b18673fbcfa7b467fec409dbf2276fb7bb8bdcdf Message-Id: <20230127203949.C53F73858416@sourceware.org> Date: Fri, 27 Jan 2023 20:39:49 +0000 (GMT) List-Id: https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=b18673fbcfa7b467fec409dbf2276fb7bb8bdcdf commit b18673fbcfa7b467fec409dbf2276fb7bb8bdcdf Author: Adhemerval Zanella Date: Tue Oct 20 13:37:15 2020 -0300 linux: Use getdents64 on non-LFS readdir The non-LFS opendir reserves a translation entry to be used to return the entry and the dirent64 struct is translated to the temporary buffer on each readdir call. Entries that overflow d_off/d_ino and the buffer reallocation failure (in case of large d_name) are ignored. Checked on x86_64-linux-gnu and i686-linux-gnu. Diff: --- dirent/tst-scandir.c | 6 ++- include/dirent.h | 2 +- sysdeps/unix/sysv/linux/dirstream.h | 5 +++ sysdeps/unix/sysv/linux/readdir.c | 83 +++++++++++++++++++++++++------------ 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/dirent/tst-scandir.c b/dirent/tst-scandir.c index 8d87d4dd74..7bc666449e 100644 --- a/dirent/tst-scandir.c +++ b/dirent/tst-scandir.c @@ -155,8 +155,12 @@ do_test (void) } if (n != 6) { + /* Non-lfs opendir skips entries that can not be represented (for + instance if d_off is not an offset but rather an internal filesystem + representation. For this case there is no point in continue the + testcase. */ printf ("scandir returned %d entries instead of 6\n", n); - return 1; + return EXIT_UNSUPPORTED; } struct diff --git a/include/dirent.h b/include/dirent.h index d7567f5e86..17827176ba 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -1,8 +1,8 @@ #ifndef _DIRENT_H +# include # ifndef _ISOMAC # include # endif -# include # ifndef _ISOMAC # include # include diff --git a/sysdeps/unix/sysv/linux/dirstream.h b/sysdeps/unix/sysv/linux/dirstream.h index 3cb313b410..adcf8234f1 100644 --- a/sysdeps/unix/sysv/linux/dirstream.h +++ b/sysdeps/unix/sysv/linux/dirstream.h @@ -18,6 +18,7 @@ #ifndef _DIRSTREAM_H #define _DIRSTREAM_H 1 +#include #include #include @@ -41,6 +42,10 @@ struct __dirstream int errcode; /* Delayed error code. */ +#if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T + struct dirent tdp; +#endif + /* Directory block. We must make sure that this block starts at an address that is aligned adequately enough to store dirent entries. Using the alignment of "void *" is not diff --git a/sysdeps/unix/sysv/linux/readdir.c b/sysdeps/unix/sysv/linux/readdir.c index 4a4c00ea07..cd0ccaf33a 100644 --- a/sysdeps/unix/sysv/linux/readdir.c +++ b/sysdeps/unix/sysv/linux/readdir.c @@ -21,42 +21,71 @@ #if !_DIRENT_MATCHES_DIRENT64 #include +/* Translate the DP64 entry to the non-LFS one in the translation entry + at dirstream DS. Return true is the translation was possible or + false if either an internal field can not be represented in the non-LFS + entry or if the name is too long. */ +static bool +dirstream_entry (struct __dirstream *ds, const struct dirent64 *dp64) +{ + /* Check for overflow. */ + if (!in_off_t_range (dp64->d_off) || !in_ino_t_range (dp64->d_ino)) + return false; + + /* And if name is too large. */ + if (dp64->d_reclen - offsetof (struct dirent64, d_name) > NAME_MAX) + return false; + + ds->filepos = dp64->d_off; + + ds->tdp.d_off = dp64->d_off; + ds->tdp.d_ino = dp64->d_ino; + ds->tdp.d_reclen = sizeof (struct dirent) + + dp64->d_reclen - offsetof (struct dirent64, d_name); + ds->tdp.d_type = dp64->d_type; + memcpy (ds->tdp.d_name, dp64->d_name, + dp64->d_reclen - offsetof (struct dirent64, d_name)); + + return true; +} + /* Read a directory entry from DIRP. */ struct dirent * __readdir_unlocked (DIR *dirp) { - struct dirent *dp; int saved_errno = errno; - if (dirp->offset >= dirp->size) + while (1) { - /* We've emptied out our buffer. Refill it. */ - - size_t maxread = dirp->allocation; - ssize_t bytes; - - bytes = __getdents (dirp->fd, dirp->data, maxread); - if (bytes <= 0) + if (dirp->offset >= dirp->size) { - /* Linux may fail with ENOENT on some file systems if the - directory inode is marked as dead (deleted). POSIX - treats this as a regular end-of-directory condition, so - do not set errno in that case, to indicate success. */ - if (bytes == 0 || errno == ENOENT) - __set_errno (saved_errno); - return NULL; - } - dirp->size = (size_t) bytes; - - /* Reset the offset into the buffer. */ - dirp->offset = 0; + /* We've emptied out our buffer. Refill it. */ + ssize_t bytes = __getdents64 (dirp->fd, dirp->data, + dirp->allocation); + if (bytes <= 0) + { + /* Linux may fail with ENOENT on some file systems if the + directory inode is marked as dead (deleted). POSIX + treats this as a regular end-of-directory condition, so + do not set errno in that case, to indicate success. */ + if (bytes < 0 && errno == ENOENT) + __set_errno (saved_errno); + return NULL; + } + dirp->size = bytes; + + /* Reset the offset into the buffer. */ + dirp->offset = 0; + } + + struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset]; + dirp->offset += dp64->d_reclen; + + /* Skip entries which might overflow d_off/d_ino or if the translation + buffer can not be resized. */ + if (dirstream_entry (dirp, dp64)) + return &dirp->tdp; } - - dp = (struct dirent *) &dirp->data[dirp->offset]; - dirp->offset += dp->d_reclen; - dirp->filepos = dp->d_off; - - return dp; } struct dirent *