From 79862beef38c85d1c74cb37fc2705d3303f96bb0 Mon Sep 17 00:00:00 2001 From: kientzle Date: Thu, 27 Nov 2008 05:49:52 +0000 Subject: 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 --- usr.bin/tar/tree.c | 35 ++++++++++++++++++++++++++++------- usr.bin/tar/tree.h | 34 +++++++++++++++++++++++----------- usr.bin/tar/write.c | 7 ++++++- 3 files changed, 57 insertions(+), 19 deletions(-) (limited to 'usr.bin/tar') diff --git a/usr.bin/tar/tree.c b/usr.bin/tar/tree.c index 4c9a4bd..de2e9ef 100644 --- a/usr.bin/tar/tree.c +++ b/usr.bin/tar/tree.c @@ -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); diff --git a/usr.bin/tar/tree.h b/usr.bin/tar/tree.h index ff38f53..09e36e4 100644 --- a/usr.bin/tar/tree.h +++ b/usr.bin/tar/tree.h @@ -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) */ diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c index 894b9ba..32df3a2 100644 --- a/usr.bin/tar/write.c +++ b/usr.bin/tar/write.c @@ -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) -- cgit v1.1