From dae8beb32c6c48c608924febeac3c27ca52e9d67 Mon Sep 17 00:00:00 2001 From: hobbs Date: Fri, 7 Oct 2005 22:35:02 +0000 Subject: * 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] --- ChangeLog | 6 +++ tests/fCmd.test | 4 +- unix/tclUnixFCmd.c | 105 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 69 insertions(+), 46 deletions(-) diff --git a/ChangeLog b/ChangeLog index aaaa289..47f87eb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2005-10-07 Jeff Hobbs + + * 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 * generic/tclPipe.c (TclCreatePipeline): Fixed [SF Tcl Bug diff --git a/tests/fCmd.test b/tests/fCmd.test index bb94d42..cb6d200 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.26.2.6 2004/11/25 11:31:31 rmax Exp $ +# RCS: @(#) $Id: fCmd.test,v 1.26.2.7 2005/10/07 22:35:03 hobbs Exp $ # if {[lsearch [namespace children] ::tcltest] == -1} { @@ -1833,7 +1833,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 a96291f..c600ff3 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.28.2.4 2005/01/10 11:21:51 dkf Exp $ + * RCS: @(#) $Id: tclUnixFCmd.c,v 1.28.2.5 2005/10/07 22:35:03 hobbs Exp $ * * Portions of this code were derived from NetBSD source code which has * the following copyright notice: @@ -124,6 +124,21 @@ CONST TclFileAttrProcs tclpFileAttrProcs[] = { }; /* + * 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: */ @@ -476,7 +491,7 @@ CopyFile(src, dst, statBufPtr) break; } } - + ckfree(buffer); close(srcFd); if ((close(dstFd) != 0) || (nread == -1)) { @@ -769,7 +784,7 @@ DoRemoveDirectory(pathPtr, recursive, errorPtr) } return result; } - + /* *--------------------------------------------------------------------------- * @@ -808,13 +823,13 @@ TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr, doRewind) * loop should be rewound whenever * traverseProc has returned TCL_OK; this is * required when traverseProc modifies the - * source hierarchy, e.g. by deleting files. */ + * source hierarchy, e.g. by deleting files. */ { Tcl_StatBuf statBuf; CONST char *source, *errfile; int result, sourceLen; int targetLen; - int needRewind; + int numProcessed = 0; Tcl_DirEntry *dirEntPtr; DIR *dirPtr; @@ -850,56 +865,58 @@ TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr, doRewind) closedir(dirPtr); return result; } - + Tcl_DStringAppend(sourcePtr, "/", 1); - sourceLen = Tcl_DStringLength(sourcePtr); + sourceLen = Tcl_DStringLength(sourcePtr); if (targetPtr != NULL) { Tcl_DStringAppend(targetPtr, "/", 1); 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; - } - - /* - * 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; - } - + 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. + */ + + 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. + */ + + Tcl_DStringSetLength(sourcePtr, sourceLen); + if (targetPtr != NULL) { + Tcl_DStringSetLength(targetPtr, targetLen); + } + if (doRewind && (numProcessed > MAX_READDIR_UNLINK_THRESHOLD)) { /* - * Remove name after slash. + * 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. */ - - Tcl_DStringSetLength(sourcePtr, sourceLen); - if (targetPtr != NULL) { - Tcl_DStringSetLength(targetPtr, targetLen); - } - } - if (needRewind) { rewinddir(dirPtr); + numProcessed = 0; } - } while (needRewind); + } closedir(dirPtr); - + /* * Strip off the trailing slash we added */ @@ -925,7 +942,7 @@ TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr, doRewind) } result = TCL_ERROR; } - + return result; } -- cgit v0.12