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]

From: hobbs <hobbs>
Date: Fri, 7 Oct 2005 22:35:33 +0000

---
 ChangeLog          |  6 ++++
 tests/fCmd.test    |  4 +--
 unix/tclUnixFCmd.c | 86 +++++++++++++++++++++++++++++++++---------------------
 3 files changed, 60 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b9b6e91..659563e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-10-07  Jeff Hobbs  <[email-masked]>
+
+	* 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 <[email-masked]>
 
 	* 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);
 
     /*
-- 
cgit v0.12