diff options
Diffstat (limited to 'unix')
-rw-r--r-- | unix/tclUnixFCmd.c | 105 |
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; } |