ach@pucc-h (Stephen Uitti) (10/11/84)
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).