Subject: Re: PR/36464 CVS commit: src/lib/libc/gen
To: None <gnats-bugs@NetBSD.org>
From: Ed Ravin <eravin@panix.com>
List: netbsd-bugs
Date: 06/12/2007 00:29:07
--rwEMma7ioTxnRzrJ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Sun, Jun 10, 2007 at 01:20:03AM +0000, Christos Zoulas wrote:
> The following reply was made to PR lib/36464; it has been noted by GNATS.
> 
> From: christos@zoulas.com (Christos Zoulas)
> To: Ed Ravin <eravin@panix.com>, gnats-bugs@NetBSD.org
> Cc: lib-bug-people@netbsd.org, gnats-admin@netbsd.org,
> 	netbsd-bugs@netbsd.org
> Subject: Re: PR/36464 CVS commit: src/lib/libc/gen
> Date: Sat, 9 Jun 2007 21:17:57 -0400
> 
>  On Jun 9,  9:00pm, eravin@panix.com (Ed Ravin) wrote:
>  -- Subject: Re: PR/36464 CVS commit: src/lib/libc/gen
>  
>  | Christos - thanks for the quick fix, I will test it out later this
>  | week.
>  
>  Great, thanks. If you send me a patch I will request a formal pullup.

See attached for a patch against 3.1.  It seems to work for the two apps
I tried that use scandir() (Pine, Midnight Commander).

I got lazy and left the "divide by 24" bit in there, but otherwise
I think it matches what you did with the current tree.


--rwEMma7ioTxnRzrJ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="scandir.c.diff"

--- scandir.c	2007/06/11 23:17:07	1.1
+++ scandir.c	2007/06/12 04:25:56
@@ -60,6 +60,34 @@
 __weak_alias(alphasort,_alphasort)
 #endif
 
+
+/*
+ * Compute an estimate of the number of entries in a directory based on
+ * the file size. Returns the estimated number of entries or 0 on failure.
+ */
+static size_t
+dirsize(int fd, size_t olen)
+{
+	struct stat stb;
+	size_t nlen;
+
+	if (fstat(fd, &stb) == -1)
+		return 0;
+	/*
+	 * Estimate the array size by taking the size of the directory file
+	 * and dividing it by a multiple of the minimum size entry. 
+	 */
+	nlen = (size_t)(stb.st_size / 24 /*_DIRENT_MINSIZE((struct dirent *)0)*/ );
+	/*
+	 * If the size turns up 0, switch to an alternate strategy and use the
+	 * file size as the number of entries like ZFS returns. If that turns
+	 * out to be 0 too return a minimum of 10 entries, plus the old length.
+	 */
+	if (nlen == 0)
+		nlen = (size_t)(stb.st_size ? stb.st_size : 10);
+	return olen + nlen;
+}
+
 /*
  * The DIRSIZ macro is the minimum record length which will hold the directory
  * entry.  This requires the amount of space in struct dirent without the
@@ -80,7 +108,6 @@
 {
 	struct dirent *d, *p, **names, **newnames;
 	size_t nitems, arraysz;
-	struct stat stb;
 	DIR *dirp;
 
 	_DIAGASSERT(dirname != NULL);
@@ -88,14 +115,10 @@
 
 	if ((dirp = opendir(dirname)) == NULL)
 		return (-1);
-	if (fstat(dirp->dd_fd, &stb) < 0)
+
+	if ((arraysz = dirsize(dirp->dd_fd, 0)) == 0)
 		goto bad;
 
-	/*
-	 * estimate the array size by taking the size of the directory file
-	 * and dividing it by a multiple of the minimum size entry. 
-	 */
-	arraysz = (size_t)(stb.st_size / 24);
 	names = malloc(arraysz * sizeof(struct dirent *));
 	if (names == NULL)
 		goto bad;
@@ -110,9 +133,8 @@
 		 * realloc the maximum size.
 		 */
 		if (nitems >= arraysz) {
-			if (fstat(dirp->dd_fd, &stb) < 0)
-				goto bad2;	/* just might have grown */
-			arraysz = (size_t)(stb.st_size / 12);
+			if ((arraysz = dirsize(dirp->dd_fd, arraysz)) == 0)
+				goto bad2;
 			newnames = realloc(names,
 			    arraysz * sizeof(struct dirent *));
 			if (newnames == NULL)

--rwEMma7ioTxnRzrJ--