rm can't recurse down deeply nested directories.

Stephen Uitti ach at pucc-h
Fri Oct 12 06:17:26 AEST 1984


Subject: rm can't recurse down deeply nested directories.

Index:	bin/rm.c 4.2BSD

Description:
	One of our friendly users ran a script that infinite 
	loops making nested directories.  'rm' couldn't
	remove the result because it ran out of open file
	descriptors.  It also has a path name length problem.
	When the path name buffer overflows, the stack becomes
	corrupted.  This messes up the recursion, often producing
	a 'core' file.  The path name length bug appears if the
	path name exceeds BUFSIZ before it runs out of open
	file descriptors (usually 17 deep).

Repeat-By:
	The following is a typical csh shell script:

	set i=20
	while ($i)
		mkdir foo$i
		cd foo$i
		echo $i > test
		@ i--
	end

	Invoke this with (for example)
	csh script
	This will create a nested directory chain in ".":
	foo20/foo19/foo18/.../foo1
	with a (short) file "test" in each (not needed, but adds
	to the fun).
	Then:
	/bin/rm -rf foo20
	You should now have some sort of error message and still
	have the foo20/foo19/.../foo1 structure.
	Incidently, if the following fix doesn't work for you, then
	you have a problem which can be quickly solved as follows:
	'cd' into the directory 4 levels (to foo17) and chant
	'rm -r *'.  Then pop back to the original level
	and chant 'rm -r foo20'.

Fix:

The file is marked with
static char *sccsid = "@(#)rm.c	4.12 (Berkeley) 6/30/83";

# replace these lines (starting at line 87) and recompile.
-			if((dirp = opendir(arg)) == NULL) {
-				printf("rm: cannot read %s?\n", arg);
-				exit(1);
-			}
-			while((dp = readdir(dirp)) != NULL) {
-				if(dp->d_ino != 0 && !dotname(dp->d_name)) {
-					sprintf(name, "%s/%s", arg, dp->d_name);
-					rm(name, fflg, rflg, iflg, level+1);
-				}
-			}
-			closedir(dirp);
# with these lines
+			if (chdir(arg) < 0) {
+				printf("rm: cannot cd to %s?\n", arg);
+				exit(1);
+			}
+			if ((dirp = opendir(".")) == NULL) {
+				printf("rm: cannot read %s?\n", arg);
+				exit(1);
+			}
+			while ((dp = readdir(dirp)) != NULL) {
+				if (dp->d_ino != 0 && !dotname(dp->d_name)) {
+					strcpy(name, dp->d_name);
+					closedir(dirp);
+					rm(name, fflg, rflg, iflg, level+1);
+					if ((dirp = opendir(".")) == NULL) {
+						printf("rm: cannot read %s?\n", arg);
+						exit(1);
+					}
+				}
+			}
+			closedir(dirp);
+			if (chdir("..") < 0) {
+				printf("rm: cannot cd to '..'?\n");
+				exit(1);
+			}

	Annoyances in the source, such as source code wrapping
	around an 80 column screen, not using the #defines for
	the arguments to access, removal of excess baggage such
	as the declaration of 'sprintf' and an extraneous "int d;"
	in rm() (one of the few things that lint(1) is good for),
	not clearly marking booleans by using a #define for TRUE
	and FALSE, using BUFSIZ where MAXPATHLEN is desired,
	speedup by making the command line variables global, and
	a lack of humorous comments (though the file is "short")
	are beyond the scope of this diff, since almost every line
	gets changed.  One may then ask at this point, is it an new
	program?  (Rhetorical question, additional flames not
	required).



More information about the Comp.bugs.4bsd.ucb-fixes mailing list