summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordas <das>2004-11-11 01:15:29 (GMT)
committerdas <das>2004-11-11 01:15:29 (GMT)
commit70c7763d03734b6a1bca07c431abc928cb7bff8a (patch)
treecf50bdc7f2686f6ac20a0fc3bf2ff63a3ff825b4
parent89675228f03f60dc605bc085eaa2ac337dcfef01 (diff)
downloadtcl-70c7763d03734b6a1bca07c431abc928cb7bff8a.zip
tcl-70c7763d03734b6a1bca07c431abc928cb7bff8a.tar.gz
tcl-70c7763d03734b6a1bca07c431abc928cb7bff8a.tar.bz2
* tests/fCmd.test:
* unix/tclUnixFCmd.c (TraverseUnixTree): added option to rewind() the readdir() loop whenever the source hierarchy has been modified by traverseProc (e.g. by deleting files); this is required to ensure complete traversal of the source hierarchy on certain filesystems like HFS+. Added test for failing recursive delete on Mac OS X that was due to this. [Bug 1034337]
-rw-r--r--tests/fCmd.test11
-rw-r--r--unix/tclUnixFCmd.c82
2 files changed, 59 insertions, 34 deletions
diff --git a/tests/fCmd.test b/tests/fCmd.test
index 0b1de7e..6d3d059 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.4 2003/10/07 15:57:36 dgp Exp $
+# RCS: @(#) $Id: fCmd.test,v 1.26.2.5 2004/11/11 01:15:29 das Exp $
#
if {[lsearch [namespace children] ::tcltest] == -1} {
@@ -1829,6 +1829,15 @@ test fCmd-20.1 {TraverseUnixTree : failure opening a subdirectory directory } \
set result
} {1}
+test fCmd-20.2 {TraverseUnixTree : recursive delete of large directory: Bug 1034337} \
+ {unix notRoot} {
+ catch {file delete -force -- tfa}
+ file mkdir tfa
+ for {set i 1} {$i <= 200} {incr i} {createfile tfa/testfile_$i}
+ set result [catch {file delete -force tfa} msg]
+ while {[catch {file delete -force tfa}]} {}
+ list $result $msg
+} {0 {}}
#
# Feature testing for TclCopyFilesCmd
diff --git a/unix/tclUnixFCmd.c b/unix/tclUnixFCmd.c
index a439511..9d709e6 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.2 2003/10/03 17:45:37 vincentdarley Exp $
+ * RCS: @(#) $Id: tclUnixFCmd.c,v 1.28.2.3 2004/11/11 01:15:29 das Exp $
*
* Portions of this code were derived from NetBSD source code which has
* the following copyright notice:
@@ -147,7 +147,7 @@ static int TraversalDelete _ANSI_ARGS_((Tcl_DString *srcPtr,
static int TraverseUnixTree _ANSI_ARGS_((
TraversalProc *traversalProc,
Tcl_DString *sourcePtr, Tcl_DString *destPtr,
- Tcl_DString *errorPtr));
+ Tcl_DString *errorPtr, int doRewind));
#ifdef PURIFY
/*
@@ -641,7 +641,7 @@ TclpObjCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
Tcl_DecrRefCount(transPtr);
}
- ret = TraverseUnixTree(TraversalCopy, &srcString, &dstString, &ds);
+ ret = TraverseUnixTree(TraversalCopy, &srcString, &dstString, &ds, 0);
Tcl_DStringFree(&srcString);
Tcl_DStringFree(&dstString);
@@ -760,7 +760,7 @@ DoRemoveDirectory(pathPtr, recursive, errorPtr)
*/
if (result == TCL_OK) {
- result = TraverseUnixTree(TraversalDelete, pathPtr, NULL, errorPtr);
+ result = TraverseUnixTree(TraversalDelete, pathPtr, NULL, errorPtr, 1);
}
if ((result != TCL_OK) && (recursive != 0)) {
@@ -793,7 +793,7 @@ DoRemoveDirectory(pathPtr, recursive, errorPtr)
*/
static int
-TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr)
+TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr, doRewind)
TraversalProc *traverseProc;/* Function to call for every file and
* directory in source hierarchy. */
Tcl_DString *sourcePtr; /* Pathname of source directory to be
@@ -803,11 +803,18 @@ TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr)
Tcl_DString *errorPtr; /* If non-NULL, uninitialized or free
* DString filled with UTF-8 name of file
* causing error. */
+ int doRewind; /* Flag indicating that to ensure complete
+ * traversal of source hierarchy, the readdir
+ * loop should be rewound whenever
+ * traverseProc has returned TCL_OK; this is
+ * required when traverseProc modifies the
+ * source hierarchy, e.g. by deleting files. */
{
Tcl_StatBuf statBuf;
CONST char *source, *errfile;
int result, sourceLen;
int targetLen;
+ int needRewind;
Tcl_DirEntry *dirEntPtr;
DIR *dirPtr;
@@ -852,36 +859,45 @@ TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr)
targetLen = Tcl_DStringLength(targetPtr);
}
- 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);
- if (result != TCL_OK) {
- break;
+ 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;
+ }
+
+ /*
+ * Remove name after slash.
+ */
+
+ Tcl_DStringSetLength(sourcePtr, sourceLen);
+ if (targetPtr != NULL) {
+ Tcl_DStringSetLength(targetPtr, targetLen);
+ }
}
-
- /*
- * Remove name after slash.
- */
-
- Tcl_DStringSetLength(sourcePtr, sourceLen);
- if (targetPtr != NULL) {
- Tcl_DStringSetLength(targetPtr, targetLen);
+ if (needRewind) {
+ rewinddir(dirPtr);
}
- }
+ } while (needRewind);
closedir(dirPtr);
/*