diff(1) vs directories

Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
From: Kenneth R Westerback
Date: Sunday, November 14, 2010 - 7:58 am

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);
 }
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
diff(1) vs directories, Kenneth R Westerback, (Sun Nov 14, 7:58 am)