NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: pkg/57145: gmake: *** INTERNAL: readdir: Operation not supported. Stop.



> Date: Tue, 20 Aug 2024 07:03:34 +0000
> From: mrg%NetBSD.org@localhost
> 
> this is a gmake bug.  it checks errno after readdir() but readdir() is
> not expected to set/clear errno for 'end of dir' condition, and thus
> converts some prior random error into a readdir error.

Not a gmake bug.  readdir is required to preserve errno in the
end-of-directory case:

DESCRIPTION

   [...]
   Applications wishing to check for error situations should set errno
   to 0 before calling readdir().  If errno is set to non-zero on
   return, an error occurred.

RETURN VALUE

   [...] When the end of the directory is encountered, a null pointer
   shall be returned and errno is not changed.

POSIX 2024: https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/readdir.html
SUSv2 1997: https://pubs.opengroup.org/onlinepubs/7908799/xsh/readdir.html

It's been this way for decades and we just screwed it up in the event
that lseek fails.

The attached patch arranges to preserve errno in the end-of-directory
case (and in the success case), and to set errno in error cases.

However, I would like to add an automatic test for this.

> Date: Fri, 30 Aug 2024 21:52:34 +0000
> From: mrg%NetBSD.org@localhost
> 
> we store lseek() into dirp->dd_seek for later usage, and in the case
> that it returns -1 and sets errno (this PR shows EOPNOTSUPP but most
> instances i've and others have seen is EINVAL - mine happens on pure
> FFS obj, not in a chroot.)

Can you share your reproducer for this?  Maybe we can adapt it to an
atf test.
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1725116374 0
#      Sat Aug 31 14:59:34 2024 +0000
# Branch trunk
# Node ID f39f4bb8989435150b682bb087c507f41adcaf8d
# Parent  9aaaa3422f6c6a29e9679ebd97936a4038ca0060
# EXP-Topic riastradh-pr57145-readdirerrno
readdir(3): Preserve errno on end-of-directory.

PR pkg/57145: gmake: *** INTERNAL: readdir: Operation not supported.
Stop.

diff -r 9aaaa3422f6c -r f39f4bb89894 lib/libc/gen/readdir.c
--- a/lib/libc/gen/readdir.c	Thu Aug 29 13:39:42 2024 +0000
+++ b/lib/libc/gen/readdir.c	Sat Aug 31 14:59:34 2024 +0000
@@ -56,35 +56,50 @@ static char sccsid[] = "@(#)readdir.c	8.
 struct dirent *
 _readdir_unlocked(DIR *dirp, int skipdeleted)
 {
+	const int saved_errno = errno;
 	struct dirent *dp;
 
 	for (;;) {
 		if (dirp->dd_loc >= dirp->dd_size) {
 			if (dirp->dd_flags & __DTF_READALL)
-				return (NULL);
+				break;
 			dirp->dd_loc = 0;
 		}
 		if (dirp->dd_loc == 0 && !(dirp->dd_flags & __DTF_READALL)) {
 			dirp->dd_seek = lseek(dirp->dd_fd, (off_t)0, SEEK_CUR);
 			dirp->dd_size = getdents(dirp->dd_fd,
 			    dirp->dd_buf, (size_t)dirp->dd_len);
-			if (dirp->dd_size <= 0)
-				return (NULL);
+			if (dirp->dd_size == 0) /* end of directory */
+				break;
+			if (dirp->dd_size == -1) /* getdents sets errno */
+				return NULL;
+			if (dirp->dd_size < 0) { /* paranoia */
+				errno = EIO;
+				return NULL;
+			}
 		}
 		dp = (struct dirent *)
 		    (void *)(dirp->dd_buf + (size_t)dirp->dd_loc);
-		if ((intptr_t)dp & _DIRENT_ALIGN(dp))/* bogus pointer check */
-			return (NULL);
+		/* bogus pointer check */
+		if ((intptr_t)dp & _DIRENT_ALIGN(dp)) {
+			errno = EIO;
+			return NULL;
+		}
 		/* d_reclen is unsigned; no need to compare it <= 0 */
-		if (dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc)
-			return (NULL);
+		if (dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc) {
+			errno = EIO;
+			return NULL;
+		}
 		dirp->dd_loc += dp->d_reclen;
 		if (dp->d_ino == 0 && skipdeleted)
 			continue;
 		if (dp->d_type == DT_WHT && (dirp->dd_flags & DTF_HIDEW))
 			continue;
-		return (dp);
+		return dp;
 	}
+
+	errno = saved_errno;
+	return NULL;
 }
 
 struct dirent *


Home | Main Index | Thread Index | Old Index