diff options
author | das <das> | 2004-11-11 01:15:29 (GMT) |
---|---|---|
committer | das <das> | 2004-11-11 01:15:29 (GMT) |
commit | 70c7763d03734b6a1bca07c431abc928cb7bff8a (patch) | |
tree | cf50bdc7f2686f6ac20a0fc3bf2ff63a3ff825b4 | |
parent | 89675228f03f60dc605bc085eaa2ac337dcfef01 (diff) | |
download | tcl-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.test | 11 | ||||
-rw-r--r-- | unix/tclUnixFCmd.c | 82 |
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); /* |