Canonicalize dirname(3) behavior.
diff --git a/archival/tar.c b/archival/tar.c
index cf65798..389d7f0 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -342,9 +342,11 @@
 	if (extractFlag==TRUE && tostdoutFlag==FALSE) {
 		/* Create the path to the file, just in case it isn't there...
 		 * This should not screw up path permissions or anything. */
-		char *dir = dirname (header->name);
+		char *buf, *dir;
+		buf = xstrdup (header->name);
+		dir = dirname (buf);
 		make_directory (dir, -1, FILEUTILS_RECUR);
-		free (dir);
+		free (buf);
 		if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, 
 						header->mode & ~S_IFMT)) < 0) {
 			error_msg(io_error, header->name, strerror(errno)); 
diff --git a/libbb/dirname.c b/libbb/dirname.c
index 5f83994..87db1f2 100644
--- a/libbb/dirname.c
+++ b/libbb/dirname.c
@@ -22,7 +22,8 @@
 #include <string.h>
 #include "libbb.h"
 
-/* Return a string on the heap containing the directory component of PATH.  */
+/* Return a string containing the path name of the parent
+ * directory of PATH.  */
 
 char *dirname(const char *path)
 {
@@ -43,7 +44,8 @@
 		s--;
 
 	if (s < path)
-		return xstrdup (".");
-	else
-		return xstrndup (path, s - path + 1);
+		return ".";
+
+	s[1] = '\0';
+	return path;
 }
diff --git a/libbb/make_directory.c b/libbb/make_directory.c
index 7b7fde9..a06a410 100644
--- a/libbb/make_directory.c
+++ b/libbb/make_directory.c
@@ -50,13 +50,17 @@
 
 		if (stat (path, &st) < 0 && errno == ENOENT) {
 			int status;
-			char *parent = dirname (path);
-			mode_t mask = umask (0);
+			char *buf, *parent;
+			mode_t mask;
+
+			mask = umask (0);
 			umask (mask);
 
+			buf = xstrdup (path);
+			parent = dirname (buf);
 			status = make_directory (parent, (0777 & ~mask) | 0300,
 					FILEUTILS_RECUR);
-			free (parent);
+			free (buf);
 
 			if (status < 0 || make_directory (path, mode, 0) < 0)
 				return -1;
diff --git a/libbb/unarchive.c b/libbb/unarchive.c
index 0d414a3..2d171b4 100644
--- a/libbb/unarchive.c
+++ b/libbb/unarchive.c
@@ -127,13 +127,15 @@
 			}
 		}
 		if (function & extract_create_leading_dirs) { /* Create leading directories with default umask */
-			char *parent = dirname(full_name);
+			char *buf, *parent;
+			buf = xstrdup(full_name);
+			parent = dirname(full_name);
 			if (make_directory (parent, -1, FILEUTILS_RECUR) != 0) {
 				if ((function & extract_quiet) != extract_quiet) {
 					error_msg("couldn't create leading directories");
 				}
 			}
-			free (parent);
+			free (buf);
 		}
 		switch(file_entry->mode & S_IFMT) {
 			case S_IFREG:
diff --git a/tar.c b/tar.c
index cf65798..389d7f0 100644
--- a/tar.c
+++ b/tar.c
@@ -342,9 +342,11 @@
 	if (extractFlag==TRUE && tostdoutFlag==FALSE) {
 		/* Create the path to the file, just in case it isn't there...
 		 * This should not screw up path permissions or anything. */
-		char *dir = dirname (header->name);
+		char *buf, *dir;
+		buf = xstrdup (header->name);
+		dir = dirname (buf);
 		make_directory (dir, -1, FILEUTILS_RECUR);
-		free (dir);
+		free (buf);
 		if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, 
 						header->mode & ~S_IFMT)) < 0) {
 			error_msg(io_error, header->name, strerror(errno));