diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | tests/fCmd.test | 4 | ||||
-rw-r--r-- | unix/tclUnixFCmd.c | 86 |
3 files changed, 60 insertions, 36 deletions
@@ -1,3 +1,9 @@ +2005-10-07 Jeff Hobbs <jeffh@ActiveState.com> + + * unix/tclUnixFCmd.c (TraverseUnixTree): Adjust 2004-11-11 change to + * tests/fCmd.test (fCmd-20.2): account for NFS special + files with a readdir rewind threshold. [Bug 1034337] + 2005-10-05 Andreas Kupries <andreask@activestate.com> * generic/tclPipe.c (TclCreatePipeline): Fixed [SF Tcl Bug diff --git a/tests/fCmd.test b/tests/fCmd.test index 4ba7034..bc7b550 100644 --- a/tests/fCmd.test +++ b/tests/fCmd.test @@ -10,7 +10,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: fCmd.test,v 1.46 2005/05/10 18:35:19 kennykb Exp $ +# RCS: @(#) $Id: fCmd.test,v 1.47 2005/10/07 22:35:33 hobbs Exp $ # if {[lsearch [namespace children] ::tcltest] == -1} { @@ -1766,7 +1766,7 @@ test fCmd-20.2 {TraverseUnixTree : recursive delete of large directory: Bug 1034 {unix notRoot} { catch {file delete -force -- tfa} file mkdir tfa - for {set i 1} {$i <= 200} {incr i} {createfile tfa/testfile_$i} + for {set i 1} {$i <= 300} {incr i} {createfile tfa/testfile_$i} set result [catch {file delete -force tfa} msg] while {[catch {file delete -force tfa}]} {} list $result $msg diff --git a/unix/tclUnixFCmd.c b/unix/tclUnixFCmd.c index 4105544..a80df2c 100644 --- a/unix/tclUnixFCmd.c +++ b/unix/tclUnixFCmd.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixFCmd.c,v 1.45 2005/09/15 16:40:03 dgp Exp $ + * RCS: @(#) $Id: tclUnixFCmd.c,v 1.46 2005/10/07 22:35:33 hobbs Exp $ * * Portions of this code were derived from NetBSD source code which has the * following copyright notice: @@ -162,6 +162,22 @@ CONST TclFileAttrProcs tclpFileAttrProcs[] = { #endif }; #endif + +/* + * This is the maximum number of consecutive readdir/unlink calls that can be + * made (with no intervening rewinddir or closedir/opendir) before triggering + * a bug that makes readdir return NULL even though some directory entries + * have not been processed. The bug afflicts SunOS's readdir when applied to + * ufs file systems and Darwin 6.5's (and OSX v.10.3.8's) HFS+. JH found the + * Darwin readdir to reset at 172, so 150 is chosen to be conservative. We + * can't do a general rewind on failure as NFS can create special files that + * recreate themselves when you try and delete them. 8.4.8 added a solution + * that was affected by a single such NFS file, this solution should not be + * affected by less than THRESHOLD such files. [Bug 1034337] + */ + +#define MAX_READDIR_UNLINK_THRESHOLD 150 + /* * Declarations for local procedures defined in this file: */ @@ -864,7 +880,7 @@ TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr, doRewind) CONST char *source, *errfile; int result, sourceLen; int targetLen; - int needRewind; + int numProcessed = 0; Tcl_DirEntry *dirEntPtr; DIR *dirPtr; @@ -909,45 +925,47 @@ TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr, doRewind) targetLen = Tcl_DStringLength(targetPtr); } - do { - needRewind = 0; - while ((dirEntPtr = TclOSreaddir(dirPtr)) != NULL) { /* INTL: Native */ - if ((dirEntPtr->d_name[0] == '.') - && ((dirEntPtr->d_name[1] == '\0') - || (strcmp(dirEntPtr->d_name, "..") == 0))) { - continue; - } + while ((dirEntPtr = TclOSreaddir(dirPtr)) != NULL) { /* INTL: Native. */ + if ((dirEntPtr->d_name[0] == '.') + && ((dirEntPtr->d_name[1] == '\0') + || (strcmp(dirEntPtr->d_name, "..") == 0))) { + continue; + } - /* - * Append name after slash, and recurse on the file. - */ + /* + * Append name after slash, and recurse on the file. + */ - Tcl_DStringAppend(sourcePtr, dirEntPtr->d_name, -1); - if (targetPtr != NULL) { - Tcl_DStringAppend(targetPtr, dirEntPtr->d_name, -1); - } - result = TraverseUnixTree(traverseProc, sourcePtr, targetPtr, - errorPtr, doRewind); - if (result != TCL_OK) { - needRewind = 0; - break; - } else { - needRewind = doRewind; - } + Tcl_DStringAppend(sourcePtr, dirEntPtr->d_name, -1); + if (targetPtr != NULL) { + Tcl_DStringAppend(targetPtr, dirEntPtr->d_name, -1); + } + result = TraverseUnixTree(traverseProc, sourcePtr, targetPtr, + errorPtr, doRewind); + if (result != TCL_OK) { + break; + } else { + numProcessed++; + } - /* - * Remove name after slash. - */ + /* + * Remove name after slash. + */ - Tcl_DStringSetLength(sourcePtr, sourceLen); - if (targetPtr != NULL) { - Tcl_DStringSetLength(targetPtr, targetLen); - } + Tcl_DStringSetLength(sourcePtr, sourceLen); + if (targetPtr != NULL) { + Tcl_DStringSetLength(targetPtr, targetLen); } - if (needRewind) { + if (doRewind && (numProcessed > MAX_READDIR_UNLINK_THRESHOLD)) { + /* + * Call rewinddir if we've called unlink or rmdir so many times + * (since the opendir or the previous rewinddir), to avoid + * a NULL-return that may a symptom of a buggy readdir. + */ rewinddir(dirPtr); + numProcessed = 0; } - } while (needRewind); + } closedir(dirPtr); /* |