svn commit: r185360 - head/usr.bin/tar

Tim Kientzle kientzle at FreeBSD.org
Wed Nov 26 21:49:53 PST 2008


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)


More information about the svn-src-head mailing list