summaryrefslogtreecommitdiffstats
path: root/unix
diff options
context:
space:
mode:
Diffstat (limited to 'unix')
-rw-r--r--unix/tclUnixFCmd.c105
1 files changed, 61 insertions, 44 deletions
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;
}