Tcl Source Code

Artifact [acca91d27f]
Login

Artifact acca91d27fc049ba6f9089a8d2ff7cc106355102:

Attachment "1034337-hobbs.patch" to ticket [1034337fff] added by hobbs 2005-10-08 05:27:42.
Index: tests/fCmd.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/fCmd.test,v
retrieving revision 1.26.2.6
diff -u -r1.26.2.6 fCmd.test
--- tests/fCmd.test	25 Nov 2004 11:31:31 -0000	1.26.2.6
+++ tests/fCmd.test	7 Oct 2005 22:26:53 -0000
@@ -1833,7 +1833,7 @@
 	{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
Index: unix/tclUnixFCmd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixFCmd.c,v
retrieving revision 1.28.2.4
diff -u -r1.28.2.4 tclUnixFCmd.c
--- unix/tclUnixFCmd.c	10 Jan 2005 11:21:51 -0000	1.28.2.4
+++ unix/tclUnixFCmd.c	7 Oct 2005 22:26:53 -0000
@@ -124,6 +124,21 @@
 };
 
 /*
+ * 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 @@
 	    break;
 	}
     }
-	
+
     ckfree(buffer);
     close(srcFd);
     if ((close(dstFd) != 0) || (nread == -1)) {
@@ -769,7 +784,7 @@
     }
     return result;
 }
-	
+
 /*
  *---------------------------------------------------------------------------
  *
@@ -808,13 +823,13 @@
     				 * 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 @@
 	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 @@
 	}
 	result = TCL_ERROR;
     }
-	    
+
     return result;
 }