The dirent functions are defined in POSIX, and POSIX clearly specifies that no POSIX function shall set errno to zero. If a caller of readdir() wants to distinguish errors from a successful end of the directory, the caller must set errno = 0 before calling readdir().
Don't set errno = ENOENT if the directory is completely empty and doesn't have even the "." and ".." entries. It may happen when reading a root directory of an empty drive. In readdir(), preserve errno even when returning non-NULL. This shouldn't be required but let's do it anyway in case something assumes it. In closedir(), preserve the errno value that _findclose() may set. In theory, free() can modify errno. It's even mentioned in Microsoft's docs. (POSIX.1-2024 requires free() to preserve errno but older ones don't, and some implementations indeed could modify errno.) While seekdir() doesn't return anything, with this implementation errors can be detected if caller first sets errno = 0. If errors occur and then readdir() is called, it will return as if the end of the directory had been successfully reached. This should be good enough here. seekdir() is rarely used. --- mingw-w64-crt/misc/dirent.c | 50 ++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/mingw-w64-crt/misc/dirent.c b/mingw-w64-crt/misc/dirent.c index 215b0b0b6..4f9c61b0c 100644 --- a/mingw-w64-crt/misc/dirent.c +++ b/mingw-w64-crt/misc/dirent.c @@ -45,8 +45,6 @@ _topendir (const _TCHAR *szPath) unsigned int rc; _TCHAR szFullPath[MAX_PATH]; - errno = 0; - if (!szPath) { errno = EFAULT; @@ -133,7 +131,11 @@ _topendir (const _TCHAR *szPath) struct _tdirent * _treaddir (_TDIR * dirp) { - errno = 0; + /* We call functions that may modify errno but we don't always + * want our caller to see a modified errno value. Specifically, + * it is essential that errno is preserved when the end of the + * directory has been successfully reached and we return NULL. */ + int finalErrno = errno; /* Check for valid DIR struct. */ if (!dirp) @@ -145,7 +147,8 @@ _treaddir (_TDIR * dirp) if (dirp->dd_stat < 0) { /* We have already returned all files in the directory - * (or the structure has an invalid dd_stat). */ + * (or the structure has an invalid dd_stat). At the end + * of the directory, errno must not be modified. */ return NULL; } else if (dirp->dd_stat == 0) @@ -156,8 +159,17 @@ _treaddir (_TDIR * dirp) if (dirp->dd_handle == -1) { - /* Whoops! Seems there are no files in that - * directory. */ + /* There are no files in the directory or an error occurred. + * _tfindfirst sets errno to ENOENT if the directory is empty + * but readdir must not do that. + * + * Note that the interesting value from GetLastError is different + * than with _tfindnext. Here even the entries "." and ".." don't + * exist, so no files can be found. The root directory of an empty + * drive is an example. */ + DWORD winerr = GetLastError (); + if (winerr != ERROR_FILE_NOT_FOUND) + finalErrno = errno; dirp->dd_stat = -1; } else @@ -171,11 +183,11 @@ _treaddir (_TDIR * dirp) if (_tfindnext (dirp->dd_handle, &(dirp->dd_dta))) { /* We are off the end or otherwise error. - _findnext sets errno to ENOENT if no more file - Undo this. */ + _tfindnext sets errno to ENOENT at the end + of the directory but readdir must not do that. */ DWORD winerr = GetLastError (); - if (winerr == ERROR_NO_MORE_FILES) - errno = 0; + if (winerr != ERROR_NO_MORE_FILES) + finalErrno = errno; _findclose (dirp->dd_handle); dirp->dd_handle = -1; dirp->dd_stat = -1; @@ -195,9 +207,14 @@ _treaddir (_TDIR * dirp) * file name. */ dirp->dd_dir.d_namlen = _tcslen (dirp->dd_dta.name); _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name); + + /* It shouldn't be necessary to preserve errno when we return non-NULL. + * Do it anyway. */ + errno = finalErrno; return &dirp->dd_dir; } + errno = finalErrno; return NULL; } @@ -210,10 +227,7 @@ _treaddir (_TDIR * dirp) int _tclosedir (_TDIR * dirp) { - int rc; - - errno = 0; - rc = 0; + int rc = 0; if (!dirp) { @@ -227,7 +241,9 @@ _tclosedir (_TDIR * dirp) } /* Delete the dir structure. */ + int savedErrno = errno; free (dirp); + errno = savedErrno; return rc; } @@ -241,8 +257,6 @@ _tclosedir (_TDIR * dirp) void _trewinddir (_TDIR * dirp) { - errno = 0; - if (!dirp) { errno = EFAULT; @@ -267,8 +281,6 @@ _trewinddir (_TDIR * dirp) long _ttelldir (_TDIR * dirp) { - errno = 0; - if (!dirp) { errno = EFAULT; @@ -289,8 +301,6 @@ _ttelldir (_TDIR * dirp) void _tseekdir (_TDIR * dirp, long lPos) { - errno = 0; - if (!dirp) { errno = EFAULT; -- 2.47.1 _______________________________________________ Mingw-w64-public mailing list Mingw-w64-public@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mingw-w64-public