libs/libc/dirent: preserve errno on readdir() end-of-directory

readdir() returned NULL at end-of-directory without ensuring errno was
clean.  POSIX requires errno to be unchanged at EOF, but the underlying
read() path may leave a stale errno value.  Callers that follow the
POSIX idiom (set errno to 0 before the call, test it after a NULL
return), such as readdir_r() and scandir(), then misread this as a
readdir() failure.

Save errno on entry and restore it on the end-of-directory return so
that EOF no longer reports a spurious error, while genuine errors
(read() returning a negative value) still propagate.

Signed-off-by: yushuailong <yyyusl@qq.com>
This commit is contained in:
yushuailong 2026-06-02 14:34:04 +08:00 committed by Xiang Xiao
parent e15672f7a4
commit 7e67d2d906

View file

@ -59,6 +59,7 @@
FAR struct dirent *readdir(DIR *dirp)
{
int errcode;
int ret;
if (!dirp)
@ -67,11 +68,22 @@ FAR struct dirent *readdir(DIR *dirp)
return NULL;
}
/* Save errno so it can be restored on end-of-directory. POSIX requires
* errno to be unchanged at EOF, but the read() path may not preserve it.
*/
errcode = get_errno();
ret = read(dirp->fd, &dirp->entry, sizeof(struct dirent));
if (ret <= 0)
if (ret == 0)
{
set_errno(errcode); /* EOF: restore errno (not an error) */
return NULL;
}
else if (ret < 0)
{
return NULL; /* error: read() already set errno */
}
return &dirp->entry;
}