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