[PATCH 6/8] scandir(3) previously used st_size

Sebastian Huber sebastian.huber@embedded-brains.de
Thu Jan 31 13:06:00 GMT 2019


From: das <das@FreeBSD.org>

to obtain an initial estimate of the array length needed to store all
the directory entries. Although BSD has historically guaranteed that
st_size is the size of the directory file, POSIX does not, and more to
the point, some recent filesystems such as ZFS use st_size to mean
something else.

The fix is to not stat the directory at all, set the initial
array size to 32 entries, and realloc it in powers of 2 if that
proves insufficient.

PR:	113668
---
 newlib/libc/posix/scandir.c | 84 +++++++++++++++++----------------------------
 1 file changed, 31 insertions(+), 53 deletions(-)

diff --git a/newlib/libc/posix/scandir.c b/newlib/libc/posix/scandir.c
index 8404cd0de..94c583761 100644
--- a/newlib/libc/posix/scandir.c
+++ b/newlib/libc/posix/scandir.c
@@ -42,8 +42,6 @@ __FBSDID("$FreeBSD$");
  * struct dirent (through namelist). Returns -1 if there were any errors.
  */
 
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <stddef.h>
 #include <dirent.h>
 #include <stdlib.h>
@@ -71,41 +69,22 @@ scandir(const char *dirname, struct dirent ***namelist,
     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
 	const struct dirent **))
 {
-	struct dirent *d, *p, **names;
-	size_t nitems;
-	struct stat stb;
-	long arraysz;
+	struct dirent *d, *p, **names = NULL;
+	size_t arraysz, numitems;
 	DIR *dirp;
-	int successful = 0;
-	int rc = 0;
-
-	dirp = NULL;
-	names = NULL;
 
 	if ((dirp = opendir(dirname)) == NULL)
 		return(-1);
 #ifdef HAVE_DD_LOCK
 	__lock_acquire_recursive(dirp->dd_lock);
 #endif
-	if (fstat(dirp->dd_fd, &stb) < 0)
-		goto cleanup;
-
-	/*
- 	 * If there were no directory entries, then bail.
- 	 */
-	if (stb.st_size == 0)
-		goto cleanup;
 
-	/*
-	 * estimate the array size by taking the size of the directory file
-	 * and dividing it by a multiple of the minimum size entry. 
-	 */
-	arraysz = (stb.st_size / 24);
+	numitems = 0;
+	arraysz = 32;	/* initial estimate of the array size */
 	names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
 	if (names == NULL)
-		goto cleanup;
+		goto fail;
 
-	nitems = 0;
 	while ((d = readdir(dirp)) != NULL) {
 		if (select != NULL && !(*select)(d))
 			continue;	/* just selected names */
@@ -114,7 +93,7 @@ scandir(const char *dirname, struct dirent ***namelist,
 		 */
 		p = (struct dirent *)malloc(DIRSIZ(d));
 		if (p == NULL)
-			goto cleanup;
+			goto fail;
 		p->d_ino = d->d_ino;
 		p->d_reclen = d->d_reclen;
 #ifdef _DIRENT_HAVE_D_NAMLEN
@@ -127,39 +106,38 @@ scandir(const char *dirname, struct dirent ***namelist,
 		 * Check to make sure the array has space left and
 		 * realloc the maximum size.
 		 */
-		if (++nitems >= arraysz) {
-			if (fstat(dirp->dd_fd, &stb) < 0)
-				goto cleanup;
-			arraysz = stb.st_size / 12;
-			names = (struct dirent **)reallocf((char *)names,
-				arraysz * sizeof(struct dirent *));
-			if (names == NULL)
-				goto cleanup;
+		if (numitems >= arraysz) {
+			struct dirent **names2;
+
+			names2 = reallocarray(names, arraysz,
+			    2 * sizeof(struct dirent *));
+			if (names2 == NULL) {
+				free(p);
+				goto fail;
+			}
+			names = names2;
+			arraysz *= 2;
 		}
-		names[nitems-1] = p;
+		names[numitems++] = p;
 	}
-	successful = 1;
-cleanup:
 	closedir(dirp);
-	if (successful) {
-		if (nitems && dcomp != NULL)
-			qsort(names, nitems, sizeof(struct dirent *), (void *)dcomp);
-		*namelist = names;
-		rc = nitems;
-	} else {  /* We were unsuccessful, clean up storage and return -1.  */
-		if ( names ) {
-			int i;
-			for (i=0; i < nitems; i++ )
-				free( names[i] );
-			free( names );
-		}
-		rc = -1;
-	}
+	if (numitems && dcomp != NULL)
+		qsort(names, numitems, sizeof(struct dirent *), (void *)dcomp);
+	*namelist = names;
+#ifdef HAVE_DD_LOCK
+	__lock_release_recursive(dirp->dd_lock);
+#endif
+	return (numitems);
 
+fail:
+	while (numitems > 0)
+		free(names[--numitems]);
+	free(names);
+	closedir(dirp);
 #ifdef HAVE_DD_LOCK
 	__lock_release_recursive(dirp->dd_lock);
 #endif
-	return(rc);
+	return (-1);
 }
 
 /*
-- 
2.16.4



More information about the Newlib mailing list