From owner-svn-src-all@FreeBSD.ORG Thu Nov 27 05:49:53 2008 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 5209C1065674; Thu, 27 Nov 2008 05:49:53 +0000 (UTC) (envelope-from kientzle@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 4D4848FC26; Thu, 27 Nov 2008 05:49:53 +0000 (UTC) (envelope-from kientzle@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id mAR5nriY077474; Thu, 27 Nov 2008 05:49:53 GMT (envelope-from kientzle@svn.freebsd.org) Received: (from kientzle@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id mAR5nrYE077471; Thu, 27 Nov 2008 05:49:53 GMT (envelope-from kientzle@svn.freebsd.org) Message-Id: <200811270549.mAR5nrYE077471@svn.freebsd.org> From: Tim Kientzle Date: Thu, 27 Nov 2008 05:49:53 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r185360 - head/usr.bin/tar X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 27 Nov 2008 05:49:53 -0000 Author: kientzle Date: Thu Nov 27 05:49:52 2008 New Revision: 185360 URL: http://svn.freebsd.org/changeset/base/185360 Log: 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 Modified: head/usr.bin/tar/tree.c head/usr.bin/tar/tree.h head/usr.bin/tar/write.c Modified: head/usr.bin/tar/tree.c ============================================================================== --- head/usr.bin/tar/tree.c Thu Nov 27 05:14:36 2008 (r185359) +++ head/usr.bin/tar/tree.c Thu Nov 27 05:49:52 2008 (r185360) @@ -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); Modified: head/usr.bin/tar/tree.h ============================================================================== --- head/usr.bin/tar/tree.h Thu Nov 27 05:14:36 2008 (r185359) +++ head/usr.bin/tar/tree.h Thu Nov 27 05:49:52 2008 (r185360) @@ -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 * /* p 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(str */ 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) */ Modified: head/usr.bin/tar/write.c ============================================================================== --- head/usr.bin/tar/write.c Thu Nov 27 05:14:36 2008 (r185359) +++ head/usr.bin/tar/write.c Thu Nov 27 05:49:52 2008 (r185360) @@ -655,8 +655,13 @@ write_hierarchy(struct bsdtar *bsdtar, s 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)