Subject: Re: PR/36464 CVS commit: src/lib/libc/gen
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Ed Ravin <eravin@panix.com>
List: netbsd-bugs
Date: 06/12/2007 04:30:04
The following reply was made to PR lib/36464; it has been noted by GNATS.

From: Ed Ravin <eravin@panix.com>
To: gnats-bugs@NetBSD.org
Cc: lib-bug-people@netbsd.org, gnats-admin@netbsd.org,
	netbsd-bugs@netbsd.org, eravin@panix.com
Subject: Re: PR/36464 CVS commit: src/lib/libc/gen
Date: Tue, 12 Jun 2007 00:29:07 -0400

 --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--