After visiting a subdirectory, we use chdir("..") or

fchdir() to return back to the parent.  If those fail,
we're just dead in the water.  Add a new error value
TREE_ERROR_FATAL to indicate that directory traversal
cannot continue.  Have write.c honor that by exiting
immediately.

MFC after:	30 days
This commit is contained in:
Tim Kientzle 2008-11-27 05:49:52 +00:00
parent e4504a7d06
commit 2f01c75815
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=185360
3 changed files with 57 additions and 19 deletions

View File

@ -227,20 +227,28 @@ tree_open(const char *path)
/*
* We've finished a directory; ascend back to the parent.
*/
static void
static int
tree_ascend(struct tree *t)
{
struct tree_entry *te;
int r = 0;
te = t->stack;
t->depth--;
if (te->flags & isDirLink) {
fchdir(te->fd);
if (fchdir(te->fd) != 0) {
t->tree_errno = errno;
r = TREE_ERROR_FATAL;
}
close(te->fd);
t->openCount--;
} else {
chdir("..");
if (chdir("..") != 0) {
t->tree_errno = errno;
r = TREE_ERROR_FATAL;
}
}
return (r);
}
/*
@ -272,6 +280,17 @@ int
tree_next(struct tree *t)
{
struct dirent *de = NULL;
int r;
/* If we're called again after a fatal error, that's an API
* violation. Just crash now. */
if (t->visit_type == TREE_ERROR_FATAL) {
const char *msg = "Unable to continue traversing"
" directory heirarchy after a fatal error.";
write(2, msg, strlen(msg));
*(int *)0 = 1; /* Deliberate SEGV; NULL pointer dereference. */
exit(1); /* In case the SEGV didn't work. */
}
/* Handle the startup case by returning the initial entry. */
if (t->flags & needsReturn) {
@ -327,10 +346,11 @@ tree_next(struct tree *t)
t->depth++;
t->d = opendir(".");
if (t->d == NULL) {
tree_ascend(t); /* Undo "chdir" */
r = tree_ascend(t); /* Undo "chdir" */
tree_pop(t);
t->tree_errno = errno;
return (t->visit_type = TREE_ERROR_DIR);
t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
return (t->visit_type);
}
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
@ -340,11 +360,12 @@ tree_next(struct tree *t)
/* We've done everything necessary for the top stack entry. */
if (t->stack->flags & needsPostVisit) {
tree_ascend(t);
r = tree_ascend(t);
tree_pop(t);
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
return (t->visit_type = TREE_POSTASCENT);
t->visit_type = r != 0 ? r : TREE_POSTASCENT;
return (t->visit_type);
}
}
return (t->visit_type = 0);

View File

@ -36,8 +36,7 @@
* * Supports very deep logical traversals. The fts package
* uses "non-chdir" approach for logical traversals. This
* package does use a chdir approach for logical traversals
* and can therefore handle pathnames much longer than
* PATH_MAX.
* and can therefore handle pathnames much longer than PATH_MAX.
* * Supports deep physical traversals "out of the box."
* Due to the memory optimizations above, there's no need to
* limit dir names to 32k.
@ -53,23 +52,31 @@ struct tree *tree_open(const char * /* pathname */);
void tree_close(struct tree *);
/*
* tree_next() returns Zero if there is no next entry, non-zero if there is.
* Note that directories are potentially visited three times. The first
* time as "regular" file. If tree_descend() is invoked at that time,
* the directory is added to a work list and will be visited two more
* times: once just after descending into the directory and again
* just after ascending back to the parent.
* tree_next() returns Zero if there is no next entry, non-zero if
* there is. Note that directories are potentially visited three
* times. Directories are always visited first as part of enumerating
* their parent. If tree_descend() is invoked at that time, the
* directory is added to a work list and will subsequently be visited
* two more times: once just after descending into the directory and
* again just after ascending back to the parent.
*
* TREE_ERROR is returned if the descent failed (because the
* TREE_ERROR_DIR is returned if the descent failed (because the
* directory couldn't be opened, for instance). This is returned
* instead of TREE_PREVISIT/TREE_POSTVISIT.
* instead of TREE_PREVISIT/TREE_POSTVISIT. TREE_ERROR_DIR is not a
* fatal error, but it does imply that the relevant subtree won't be
* visited. TREE_ERROR_FATAL is returned for an error that left the
* traversal completely hosed. Right now, this is only returned for
* chdir() failures during ascent.
*/
#define TREE_REGULAR 1
#define TREE_POSTDESCENT 2
#define TREE_POSTASCENT 3
#define TREE_ERROR_DIR -1
#define TREE_ERROR_FATAL -2
int tree_next(struct tree *);
/* Errno value associated with the last traversal error. */
int tree_errno(struct tree *);
/*
@ -85,7 +92,9 @@ void tree_descend(struct tree *);
* Return information about the current entry.
*/
/* Current depth in the traversal. */
int tree_current_depth(struct tree *);
/*
* The current full pathname, length of the full pathname,
* and a name that can be used to access the file.
@ -95,6 +104,7 @@ int tree_current_depth(struct tree *);
const char *tree_current_path(struct tree *);
size_t tree_current_pathlen(struct tree *);
const char *tree_current_access_path(struct tree *);
/*
* Request the lstat() or stat() data for the current path. Since the
* tree package needs to do some of this anyway, and caches the
@ -103,7 +113,9 @@ const char *tree_current_access_path(struct tree *);
*/
const struct stat *tree_current_stat(struct tree *);
const struct stat *tree_current_lstat(struct tree *);
/* The following tests may use mechanisms much faster than stat()/lstat(). */
/* The following functions use tricks to avoid a certain number of
* stat()/lstat() calls. */
/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
int tree_current_is_physical_dir(struct tree *);
/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */

View File

@ -655,8 +655,13 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
const struct stat *st = NULL, *lst = NULL;
int descend;
if (tree_ret == TREE_ERROR_FATAL)
bsdtar_errc(bsdtar, 1, tree_errno(tree),
"%s: Unable to continue traversing directory tree",
name);
if (tree_ret == TREE_ERROR_DIR) {
bsdtar_warnc(bsdtar, errno, "%s: Couldn't visit directory", name);
bsdtar_warnc(bsdtar, errno,
"%s: Couldn't visit directory", name);
bsdtar->return_value = 1;
}
if (tree_ret != TREE_REGULAR)