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:
parent
e4504a7d06
commit
2f01c75815
@ -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);
|
||||
|
@ -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) */
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user