From 740b782d72d5891cc221997d329a66754ee437c5 Mon Sep 17 00:00:00 2001 From: das Date: Thu, 11 Nov 2004 01:14:29 +0000 Subject: * 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] --- tests/fCmd.test | 12 +++++++- unix/tclUnixFCmd.c | 82 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/tests/fCmd.test b/tests/fCmd.test index 6ff3872..1c234b3 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.43 2004/10/31 19:12:34 dkf Exp $ +# RCS: @(#) $Id: fCmd.test,v 1.44 2004/11/11 01:14:29 das Exp $ # if {[lsearch [namespace children] ::tcltest] == -1} { @@ -1762,6 +1762,16 @@ 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 f0a69b7..f0f852e 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.39 2004/10/06 16:08:57 dgp Exp $ + * RCS: @(#) $Id: tclUnixFCmd.c,v 1.40 2004/11/11 01:14:41 das Exp $ * * Portions of this code were derived from NetBSD source code which has * the following copyright notice: @@ -190,7 +190,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 /* @@ -691,7 +691,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); @@ -810,7 +810,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)) { @@ -843,7 +843,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 @@ -853,11 +853,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; @@ -902,36 +909,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); /* -- cgit v0.12