As Espie found:
Turns out diff is expecting scandir to return NULL-terminated arrays.
But scandir doesn't ! most specifically, when we fill our buffer exactly,
then we're out of luck. This doesn't happen often, but usually enough to
kill a diff -ur on a large tree with MALLOC_OPTIONS=S
Here's a diff, mostly from Espie, that seems to fix it for me.
Thoughts? Improvements? OK?
.... Ken
Index: diffdir.c
===================================================================
RCS file: /cvs/src/usr.bin/diff/diffdir.c,v
retrieving revision 1.39
diff -u -p -r1.39 diffdir.c
--- diffdir.c 8 Nov 2010 15:49:13 -0000 1.39
+++ diffdir.c 14 Nov 2010 14:21:43 -0000
@@ -38,7 +38,7 @@
#include "xmalloc.h"
static int selectfile(struct dirent *);
-static struct dirent **slurpdir(char *, int);
+static struct dirent **slurpdir(char *, int, struct dirent ***);
static void diffit(struct dirent *, char *, size_t, char *, size_t, int);
#define d_status d_type /* we need to store status for -l */
@@ -51,6 +51,7 @@ diffdir(char *p1, char *p2, int flags)
{
struct dirent *dent1, **dp1, **dirp1 = NULL;
struct dirent *dent2, **dp2, **dirp2 = NULL;
+ struct dirent **end1, **end2;
size_t dirlen1, dirlen2;
char path1[MAXPATHLEN], path2[MAXPATHLEN];
int pos;
@@ -77,8 +78,8 @@ diffdir(char *p1, char *p2, int flags)
}
/* get a list of the entries in each directory */
- dp1 = dirp1 = slurpdir(path1, Nflag + Pflag);
- dp2 = dirp2 = slurpdir(path2, Nflag);
+ dp1 = dirp1 = slurpdir(path1, Nflag + Pflag, &end1);
+ dp2 = dirp2 = slurpdir(path2, Nflag, &end2);
if (dirp1 == NULL || dirp2 == NULL)
goto closem;
@@ -86,16 +87,16 @@ diffdir(char *p1, char *p2, int flags)
* If we were given a starting point, find it.
*/
if (start != NULL) {
- while (*dp1 != NULL && strcmp((*dp1)->d_name, start) < 0)
+ while (dp1 != end1 && strcmp((*dp1)->d_name, start) < 0)
dp1++;
- while (*dp2 != NULL && strcmp((*dp2)->d_name, start) < 0)
+ while (dp2 != end2 && strcmp((*dp2)->d_name, start) < 0)
dp2++;
}
/*
* Iterate through the two directory lists, diffing as we go.
*/
- while (*dp1 != NULL || *dp2 != NULL) {
+ while (dp1 != end1 || dp2 != end2) {
dent1 = *dp1;
dent2 = *dp2;
@@ -131,11 +132,13 @@ diffdir(char *p1, char *p2, int flags)
if (lflag) {
path1[dirlen1] = '\0';
path2[dirlen2] = '\0';
- for (dp1 = dirp1; (dent1 = *dp1) != NULL; dp1++) {
+ for (dp1 = dirp1; dp1 != end1; dp1++) {
+ dent1 = *dp1;
print_status(dent1->d_status, path1, path2,
dent1->d_name);
}
- for (dp2 = dirp2; (dent2 = *dp2) != NULL; dp2++) {
+ for (dp2 = dirp2; dp2 != end2; dp2++) {
+ dent2 = *dp2;
if (dent2->d_status == D_ONLY)
print_status(dent2->d_status, path2, NULL,
dent2->d_name);
@@ -144,13 +147,13 @@ diffdir(char *p1, char *p2, int flags)
closem:
if (dirp1 != NULL) {
- for (dp1 = dirp1; (dent1 = *dp1) != NULL; dp1++)
- xfree(dent1);
+ for (dp1 = dirp1; dp1 != end1; dp1++)
+ xfree(*dp1);
xfree(dirp1);
}
if (dirp2 != NULL) {
- for (dp2 = dirp2; (dent2 = *dp2) != NULL; dp2++)
- xfree(dent2);
+ for (dp2 = dirp2; dp2 != end2; dp2++)
+ xfree(*dp2);
xfree(dirp2);
}
}
@@ -161,7 +164,7 @@ closem:
* Caller is responsible for free()ing each array element and the array itself.
*/
static struct dirent **
-slurpdir(char *dirname, int enoentok)
+slurpdir(char *dirname, int enoentok, struct dirent ***endp)
{
struct dirent **namelist = NULL;
int rval;
@@ -174,8 +177,9 @@ slurpdir(char *dirname, int enoentok)
} else {
warn("%s", dirname);
}
+ rval = 0;
}
-
+ *endp = namelist + rval;
return (namelist);
}