summaryrefslogtreecommitdiffstats
path: root/usr.bin/tar
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2008-11-27 05:49:52 +0000
committerkientzle <kientzle@FreeBSD.org>2008-11-27 05:49:52 +0000
commit79862beef38c85d1c74cb37fc2705d3303f96bb0 (patch)
treea50f2903730c3a9a069977568987a9bf9a53ad02 /usr.bin/tar
parentcfaf87c8f3aa634237236a3928a357096bb9381a (diff)
downloadFreeBSD-src-79862beef38c85d1c74cb37fc2705d3303f96bb0.zip
FreeBSD-src-79862beef38c85d1c74cb37fc2705d3303f96bb0.tar.gz
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
Diffstat (limited to 'usr.bin/tar')
-rw-r--r--usr.bin/tar/tree.c35
-rw-r--r--usr.bin/tar/tree.h34
-rw-r--r--usr.bin/tar/write.c7
3 files changed, 57 insertions, 19 deletions
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)
OpenPOWER on IntegriCloud