Merge code from upstream libtar + bug fixes

All updates and fixes applied from upstream libtar as of
March 1, 2016.

Debug flag is disabled, however non-debug output now
provides 1 line of useful output per object extracted.

I've also merged some fixes from CyanogenMod's
fork of libtar:

From: Tom Marshall <tdm@cyngn.com>
Date: Thu, 11 Feb 2016 16:24:40 -0800
Subject: libtar: Cleanup, secure, and extend numeric fields
Commit: e18b457ea1cbf6be1adc3b75450ed1c737cd82ea

From: Tom Marshall <tdm@cyngn.com>
Date: Thu, 11 Feb 2016 12:49:30 -0800
Subject: libtar: Make file sizes 64-bit clean
Commit: e628c2025549a24018bc568351465130a05daafb

From: Tom Marshall <tdm@cyngn.com>
Date: Thu, 17 Apr 2014 09:39:25 -0700
Subject: libtar: Add methods for in-memory files
Commit: 8ec5627a8ff0a91724c6d5b344f0e887da922527

From: Tom Marshall <tdm@cyngn.com>
Date: Wed, 2 Jul 2014 09:34:40 -0700
Subject: libtar: Fix hardlink extract
Commit: 166d83a51e0c51abcea37694dbd7df92d03c1f56

From: philz-cwm6 <phytowardt@gmail.com>
Date: Sat, 26 Apr 2014 01:11:35 +0200
Subject: libtar: Various bug fixes and enhancements
Commit: a271d763e94235ccee9ecaabdb52bf4b9b2f8c06
(Some of this was not merged in, as better solutions were
available from upstream libtar)

From: Tom Marshall <tdm@cyngn.com>
Date: Wed, 9 Apr 2014 09:35:54 -0700
Subject: libtar: Add const qualifiers to reduce compile warnings
Commit: 0600afa19fe827d06d3fcf24a7aabd52dbf487b4

Change-Id: I6d008cb6fdf950f835bbed63aeb8727cc5c86083
diff --git a/libtar/ChangeLog b/libtar/ChangeLog
index cde7675..03bef68 100644
--- a/libtar/ChangeLog
+++ b/libtar/ChangeLog
@@ -1,15 +1,335 @@
-2002-12-09	added list_empty() and hash_empty() functions
+	NOTE:
+	All releases below marked (Chris Frey) are maintenance releases
+	done by Chris Frey, temporarily stepping in for Mark Roth.
+	These releases are git-based only and can be found at:
+		http://repo.or.cz/w/libtar.git
 
-2002-09-12	fixed list_iterate function to return -1 if it gets
-		an invalid argument
+	Both git downloads and tarball downloads are possible at this site.
 
-		include <config.h> and <compat.h> from source files, not
-		from header file, since header file is sometimes
-		installed as part of a user-visible API
-		(those APIs should eventually be redesigned without the
-		listhash code being publicly visible, but for now we
-		need to accomodate this)
 
-2002-07-07	modified list iterate function to return int
-		(returns -1 if plugin function returns -1)
+libtar 1.2.20 - 2013/10/09 (Chris Frey)
+-------------
+      Added extern "C" protectors to listhash.h
+      Added autoconf checks for __thread compiler support
+      Fixed size_t overflow bug, as reported by Timo Warns
+      Fixed thread-safe bug in th_get_pathname() (Sergey Zhitomirsky)
+
+
+libtar 1.2.19 - 2012/12/11 (Chris Frey)
+-------------
+      Removed varargs.h and all dependencies, to avoid user compile errors
+
+      Fixed some short int / int compiler warnings in va_arg() usage
+
+      Fixed some gcc built-in compiler warnings
+
+      Changed autoconf support code from AC_RUN_ to AC_COMPILE_ to fix
+      issues reported during cross-compiling.
+
+      Applied most of Jan Cermak's const char* function argument patch.
+
+
+libtar 1.2.18 - 2012/08/02 (Chris Frey)
+-------------
+      Added more forgiving CRC checking logic when reading tar files
+
+      Note: If your application uses the macro th_crc_ok(), then to gain full
+      advantage of the changes in this version, you will need to recompile
+      your application against the new headers.  Otherwise, the library is
+      drop-in replaceable, as usual.
+
+
+libtar 1.2.17 - 2012/07/24 (Chris Frey)
+-------------
+      Applied Tim Band's checksum patch from mailing list (thanks!)
+
+
+libtar 1.2.16 - 2012/05/17 (Chris Frey)
+-------------
+      Fixed build system to allow for out-of-source tree builds
+
+
+libtar 1.2.15 - 2012/05/10 (Chris Frey)
+-------------
+Chris Frey (1):
+      Fixed harmless buffer overflow which is caught by FORTIFY on some systems
+
+
+libtar 1.2.14 - 2011/12/22 (Chris Frey)
+-------------
+Chris Frey (1):
+      Fixed truncation check, so 100 char names get GNU extension support when enabled
+
+
+libtar 1.2.13 - 2011/06/13 (Chris Frey)
+-------------
+Chris Frey (10):
+      Fixed incorrect URL in readme
+      Added autoconf/ as macro dir
+      Added autogen.sh script to build a fresh configure
+      Renamed autoconf/aclocal.m4 to psg.m4 so aclocal isn't so confused
+      Removed m4 includes, and straightened out [] m4 quoting for modern autoconfs
+      Removed auto-generated files
+      Added datarootdir to Makefile.in's
+      Fixed header warnings
+      Applied Marcin Gibula's patch fixing tar_extract_glob()
+      Changed root Makefile.in to Makefile.am, which make autoreconf workable
+
+Glenn McGrath (1):
+      Use libtool to build dynamic library
+
+James Morrison (1):
+      Document stupidity of tartype_t in libtar.c.
+
+Magnus Holmgren (1):
+      Escape hyphens that should be minus signs in man pages.
+
+Per Lidén (2):
+      Fix memory leak in th_get_pathname
+      Reduce memory used by libtar when extracting files.
+
+------------------------------------------------------------------------------
+
+libtar 1.2.11 - 3/2/03
+-------------
+
+- updated autoconf macros, compat code, and listhash code
+- fixed tar_extract_regfile() to pass mode argument to open()
+  (caused EPERM on Solaris NFS clients)
+- updated README
+
+------------------------------------------------------------------------------
+
+libtar 1.2.10 - 12/15/02
+-------------
+
+- updated README
+- minor Makefile fixes
+- fixed TH_ISREG() macro to not return true for hard links
+
+------------------------------------------------------------------------------
+
+libtar 1.2.9 - 11/19/02
+------------
+
+- fixed th_read() to return 1 on EOF
+  (thanks to Yves Crespin <Crespin.Quartz@WANADOO.FR> for the bug report)
+- minor portability fixes
+  (thanks to Yves Crespin <Crespin.Quartz@WANADOO.FR> for the bug report)
+- fixed segfault on extracting filenames with 8-bit ASCII characters
+  (thanks to Per Liden <per@FUKT.BTH.SE> for the patch)
+- fixed TH_ISDIR() macro and th_get_mode() function to handle old
+  archives that don't set the typeflag field right for directories
+- use 0777 instead of 0755 in mkdirhier()
+  (thanks to Yves Crespin <Crespin.Quartz@WANADOO.FR> for the bug report)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.8 - 9/13/02
+------------
+
+- added "-I../listhash" to CPPFLAGS in libtar/Makefile.in
+  (thanks to Kris Warkentin <kewarken@QNX.COM> for the bug report)
+- added .PHONY target to Makefile.in
+  (thanks to Steven Engelhardt <sengelha@YAHOO.COM> for the bug report)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.7 - 9/12/02
+------------
+
+- fixed minor bugs in listhash code
+  (thanks to Jim Knoble <jmknoble@pobox.com> for the bug reports)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.6 - 9/10/02
+------------
+
+- updated COPYRIGHT file
+- do not check magic field by default
+  (replaced TAR_IGNORE_MAGIC option with TAR_CHECK_MAGIC to enable check)
+- fixed th_get_mode() not to modify S_IFMT bits if they were already set
+- fixed TH_IS*() macros to check the S_IFMT mode bits in addition to typeflag
+  (this allows us to handle old tar archives that set mode bits but not
+  typeflag field for directories and other special files)
+- updated to autoconf-2.53
+- restructured autoconf macros
+- added "b" to gzoflags in gzopen_frontend() for win32 compatibility
+  (thanks to Kris Eric Warkentin <kewarken@QNX.COM> for reporting this)
+- if O_BINARY is defined (as on win32), set that bit in oflags in tar_open()
+  (thanks to Kris Eric Warkentin <kewarken@QNX.COM> for reporting this)
+- also use O_BINARY in when calling open() from tar_extract_regfile()
+  (based on patch from Graeme Peterson <gp@qnx.com>)
+- added COMPAT_FUNC_MAKEDEV macro to handle 3-arg version of makedev()
+  (based on patch from Graeme Peterson <gp@qnx.com>)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.5 - 2/20/02
+------------
+
+- updated to autoconf-2.52
+- improved Makefile portability
+- fixed memory leak in hard-link detection code
+  (thanks to Michael Kamp <kamp@HITT.NL> for the bug report)
+- fixed memory leak in symlink handling code
+  (thanks to Michael Kamp <kamp@HITT.NL> for the bug report)
+- fixed memory leak in GNU long filename code
+
+------------------------------------------------------------------------------
+
+libtar 1.2.4 - 7/24/01
+------------
+
+- code cleanups to make gcc -Wall happy
+  (thanks to Jim Knoble <jmknoble@POBOX.COM> for the patch)
+- call utime() before chmod() in tar_set_file_perms() for cygwin
+  (thanks to Kris Eric Warkentin <kewarken@QNX.COM> for reporting this)
+- added "-g" flag to trigger GNU extensions in libtar binary
+- fixed buffer termination bugs in POSIX filename prefix encoding
+  (thanks to Joerg Schilling <schilling@fokus.gmd.de> for reporting this)
+- fixed bug in th_crc_calc() for filenames with 8-bit ASCII characters
+  (thanks to Hamdouni El Bachir <bach@zehc.net> for reporting the bug
+   and Antoniu-George SAVU <santoniu@libertysurf.fr> for the patch)
+- fixed backwards conditional expression in th_read()
+  (thanks to Antoniu-George SAVU <santoniu@LIBERTYSURF.FR> for the patch)
+- added new tar_open() options to replace compile-time settings:
+  TAR_IGNORE_EOT, TAR_IGNORE_MAGIC, TAR_CHECK_VERSION, TAR_IGNORE_CRC
+  (based on feedback from Kris Eric Warkentin <kewarken@QNX.COM>)
+
+------------------------------------------------------------------------------
+
+libtar 1.2.3 - 6/26/01
+------------
+
+- misc portability fixes for OpenBSD
+- fixed libtar.h to work with C++ programs
+- fixed tar_extract_file() to properly check for pre-existing symlinks
+  (based on patch from Per Lid?n <per@fukt.hk-r.se>)
+- fixed hash creation in tar_init()
+- replaced mkdirhier() with non-recursive version
+- updated autoconf macros, compat code, and listhash code
+- reformatted code for readability
+
+------------------------------------------------------------------------------
+
+libtar 1.2.2 - 1/12/01
+------------
+
+- fixed th_print_long_ls() to not truncate user and group names
+- code cleanups to make -Wall happy
+
+------------------------------------------------------------------------------
+
+libtar 1.2.1 - 1/8/01
+------------
+
+- updated WSG_ENCAP autoconf macro
+- fixed autoconf macros to behave properly when a config.cache file
+  is present
+- fixed doc/Makefile.in to create links during compilation, not
+  installation
+- fixed listhash manpage .so link lists
+
+------------------------------------------------------------------------------
+
+libtar 1.2 - 1/4/01
+----------
+
+- minor code cleanups
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b8 - 1/2/01
+-------------
+
+- updated WSG_ENCAP autoconf macro
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b7 - 12/13/00
+-------------
+
+- fixed autoconf snprintf() test to make sure it NUL-terminates
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b6 - 11/30/00
+-------------
+
+- added $(DESTDIR) to Makefiles
+- Makefile changes to support WSG_PKG and WSG_ENCAP autoconf macros
+- changed lib/output.c to use strftime() where available
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b5 - 10/29/00
+-------------
+
+- Makefile fix
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b4 - 10/29/00
+-------------
+
+- more directory reorganization
+- minor Makefile cleanups
+- minor portability fixes
+- added function typecasting to avoid compiler warnings
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b3 - 10/26/00
+-------------
+
+- updated aclocal.m4
+- updated README
+- updated manpages
+- minor directory structure changes because of CVS setup
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b2 - 10/5/00
+-------------
+
+- added --without-zlib configure option
+- minor portability fixes
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b1 - 8/21/00
+-------------
+
+- API changes:
+  - implemented tar_fdopen()
+  - implemented tar_fd()
+  - added TAR **t argument to tar_open() instead of returning dynamic memory
+  - if TAR_NOOVERWRITE is set in options and O_CREAT is set in oflags,
+    tar_open() automatically sets O_EXCL as well
+
+------------------------------------------------------------------------------
+
+libtar 1.1.b0 - 7/10/00
+-------------
+
+- API changes:
+  - replaced internal table of tar file types with a tartype_t passed to
+    tar_open() by the caller
+    (allows file access methods to be defined dynamically)
+  - fixed tar_append_tree() to grok normal files as well as directories
+  - replaced mk_dirs_for_file() with mkdirhier() from epkg
+  - replaced strtok_r() with strsep()
+  - updated list/hash code to new interface
+
+- autoconf changes:
+  - added aclocal.m4 to clean up configure.in
+  - minor portability fixes related to lib/fnmatch.c
+
+- fixed a bug in tar_open() where the result of open() was being
+  checked for 0 instead of -1 to detect error
+
+- updated libtar driver program to handle both .tar.gz and ordinary .tar
+  via the -z option
 
diff --git a/libtar/append.c b/libtar/append.c
index 1831990..4be679c 100644
--- a/libtar/append.c
+++ b/libtar/append.c
@@ -13,8 +13,11 @@
 #include <internal.h>
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <time.h>
 #include <sys/param.h>
 #include <sys/types.h>
 
@@ -28,7 +31,7 @@
 #endif
 
 #ifdef HAVE_SELINUX
-#include "selinux/selinux.h"
+# include "selinux/selinux.h"
 #endif
 
 struct tar_dev
@@ -57,7 +60,7 @@
 
 /* appends a file to the tar archive */
 int
-tar_append_file(TAR *t, char *realname, char *savename)
+tar_append_file(TAR *t, const char *realname, const char *savename)
 {
 	struct stat s;
 	int i;
@@ -82,38 +85,46 @@
 
 	/* set header block */
 #ifdef DEBUG
-	puts("    tar_append_file(): setting header block...");
+	puts("tar_append_file(): setting header block...");
 #endif
 	memset(&(t->th_buf), 0, sizeof(struct tar_header));
 	th_set_from_stat(t, &s);
 
 	/* set the header path */
 #ifdef DEBUG
-	puts("    tar_append_file(): setting header path...");
+	puts("tar_append_file(): setting header path...");
 #endif
 	th_set_path(t, (savename ? savename : realname));
 
 #ifdef HAVE_SELINUX
 	/* get selinux context */
-	if(t->options & TAR_STORE_SELINUX) {
-		if(t->th_buf.selinux_context != NULL) {
+	if (t->options & TAR_STORE_SELINUX)
+	{
+		if (t->th_buf.selinux_context != NULL)
+		{
 			free(t->th_buf.selinux_context);
 			t->th_buf.selinux_context = NULL;
 		}
 
 		security_context_t selinux_context = NULL;
-		if (lgetfilecon(realname, &selinux_context) >= 0) {
+		if (lgetfilecon(realname, &selinux_context) >= 0)
+		{
 			t->th_buf.selinux_context = strdup(selinux_context);
-			printf("setting selinux context: %s\n", selinux_context);
+			printf("  ==> set selinux context: %s\n", selinux_context);
 			freecon(selinux_context);
 		}
 		else
+		{
+#ifdef DEBUG
 			perror("Failed to get selinux context");
+#endif
+		}
 	}
 #endif
+
 	/* check if it's a hardlink */
 #ifdef DEBUG
-	puts("    tar_append_file(): checking inode cache for hardlink...");
+	puts("tar_append_file(): checking inode cache for hardlink...");
 #endif
 	libtar_hashptr_reset(&hp);
 	if (libtar_hash_getkey(t->h, &hp, &(s.st_dev),
@@ -171,7 +182,7 @@
 			i = MAXPATHLEN - 1;
 		path[i] = '\0';
 #ifdef DEBUG
-		printf("    tar_append_file(): encoding symlink \"%s\" -> "
+		printf("tar_append_file(): encoding symlink \"%s\" -> "
 		       "\"%s\"...\n", realname, path);
 #endif
 		th_set_link(t, path);
@@ -179,10 +190,10 @@
 
 	/* print file info */
 	if (t->options & TAR_VERBOSE)
-		th_print_long_ls(t);
+		printf("%s\n", th_get_pathname(t));
 
 #ifdef DEBUG
-	puts("    tar_append_file(): writing header");
+	puts("tar_append_file(): writing header");
 #endif
 	/* write header */
 	if (th_write(t) != 0)
@@ -193,7 +204,7 @@
 		return -1;
 	}
 #ifdef DEBUG
-	puts("    tar_append_file(): back from th_write()");
+	puts("tar_append_file(): back from th_write()");
 #endif
 
 	/* if it's a regular file, write the contents as well */
@@ -229,14 +240,19 @@
 
 /* add file contents to a tarchive */
 int
-tar_append_regfile(TAR *t, char *realname)
+tar_append_regfile(TAR *t, const char *realname)
 {
 	char block[T_BLOCKSIZE];
 	int filefd;
-	int j;
-	size_t size, i;
+	int64_t i, size;
+	ssize_t j;
+	int rv = -1;
 
+#if defined(O_BINARY)
+	filefd = open(realname, O_RDONLY|O_BINARY);
+#else
 	filefd = open(realname, O_RDONLY);
+#endif
 	if (filefd == -1)
 	{
 #ifdef DEBUG
@@ -253,25 +269,83 @@
 		{
 			if (j != -1)
 				errno = EINVAL;
-			return -1;
+			goto fail;
 		}
 		if (tar_block_write(t, &block) == -1)
-			return -1;
+			goto fail;
 	}
 
 	if (i > 0)
 	{
 		j = read(filefd, &block, i);
 		if (j == -1)
+			goto fail;
+		memset(&(block[i]), 0, T_BLOCKSIZE - i);
+		if (tar_block_write(t, &block) == -1)
+			goto fail;
+	}
+
+	/* success! */
+	rv = 0;
+fail:
+	close(filefd);
+
+	return rv;
+}
+
+
+/* add file contents to a tarchive */
+int
+tar_append_file_contents(TAR *t, const char *savename, mode_t mode,
+                         uid_t uid, gid_t gid, void *buf, size_t len)
+{
+	struct stat st;
+
+	memset(&st, 0, sizeof(st));
+	st.st_mode = S_IFREG | mode;
+	st.st_uid = uid;
+	st.st_gid = gid;
+	st.st_mtime = time(NULL);
+	st.st_size = len;
+
+	th_set_from_stat(t, &st);
+	th_set_path(t, savename);
+
+	/* write header */
+	if (th_write(t) != 0)
+	{
+#ifdef DEBUG
+		fprintf(stderr, "tar_append_file_contents(): could not write header, t->fd = %d\n", t->fd);
+#endif
+		return -1;
+	}
+
+	return tar_append_buffer(t, buf, len);
+}
+
+int
+tar_append_buffer(TAR *t, void *buf, size_t len)
+{
+	char block[T_BLOCKSIZE];
+	int filefd;
+	int i, j;
+	size_t size = len;
+
+	for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE)
+	{
+		if (tar_block_write(t, buf) == -1)
 			return -1;
+		buf = (char *)buf + T_BLOCKSIZE;
+	}
+
+	if (i > 0)
+	{
+		memcpy(block, buf, i);
 		memset(&(block[i]), 0, T_BLOCKSIZE - i);
 		if (tar_block_write(t, &block) == -1)
 			return -1;
 	}
 
-	close(filefd);
-
 	return 0;
 }
 
-
diff --git a/libtar/basename.c b/libtar/basename.c
index 2ac1e13..32108d6 100644
--- a/libtar/basename.c
+++ b/libtar/basename.c
@@ -64,7 +64,7 @@
 	while (startp > path && *(startp - 1) != '/')
 		startp--;
 
-	if (endp - startp + 1 > sizeof(bname)) {
+	if (endp - startp + 1 > (int)sizeof(bname)) {
 		errno = ENAMETOOLONG;
 		return(NULL);
 	}
diff --git a/libtar/block.c b/libtar/block.c
index 6ed9e60..5d3c9d8 100644
--- a/libtar/block.c
+++ b/libtar/block.c
@@ -11,7 +11,6 @@
 */
 
 #include <internal.h>
-#include <stdio.h>
 #include <errno.h>
 
 #ifdef STDC_HEADERS
@@ -27,6 +26,17 @@
 #define SELINUX_TAG_LEN 21
 
 /* read a header block */
+/* FIXME: the return value of this function should match the return value
+	  of tar_block_read(), which is a macro which references a prototype
+	  that returns a ssize_t.  So far, this is safe, since tar_block_read()
+	  only ever reads 512 (T_BLOCKSIZE) bytes at a time, so any difference
+	  in size of ssize_t and int is of negligible risk.  BUT, if
+	  T_BLOCKSIZE ever changes, or ever becomes a variable parameter
+	  controllable by the user, all the code that calls it,
+	  including this function and all code that calls it, should be
+	  fixed for security reasons.
+	  Thanks to Chris Palmer for the critique.
+*/
 int
 th_read_internal(TAR *t)
 {
@@ -93,8 +103,8 @@
 int
 th_read(TAR *t)
 {
-	int i, j;
-	size_t sz;
+	int i;
+	size_t sz, j, blocks;
 	char *ptr;
 
 #ifdef DEBUG
@@ -126,21 +136,26 @@
 	if (TH_ISLONGLINK(t))
 	{
 		sz = th_get_size(t);
-		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+		blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+		if (blocks > ((size_t)-1 / T_BLOCKSIZE))
+		{
+			errno = E2BIG;
+			return -1;
+		}
 #ifdef DEBUG
 		printf("    th_read(): GNU long linkname detected "
-		       "(%ld bytes, %d blocks)\n", sz, j);
+		       "(%ld bytes, %d blocks)\n", sz, blocks);
 #endif
-		t->th_buf.gnu_longlink = (char *)malloc(j * T_BLOCKSIZE);
+		t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
 		if (t->th_buf.gnu_longlink == NULL)
 			return -1;
 
-		for (ptr = t->th_buf.gnu_longlink; j > 0;
-		     j--, ptr += T_BLOCKSIZE)
+		for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks;
+		     j++, ptr += T_BLOCKSIZE)
 		{
 #ifdef DEBUG
 			printf("    th_read(): reading long linkname "
-			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+			       "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
 #endif
 			i = tar_block_read(t, ptr);
 			if (i != T_BLOCKSIZE)
@@ -171,21 +186,26 @@
 	if (TH_ISLONGNAME(t))
 	{
 		sz = th_get_size(t);
-		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+		blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+		if (blocks > ((size_t)-1 / T_BLOCKSIZE))
+		{
+			errno = E2BIG;
+			return -1;
+		}
 #ifdef DEBUG
 		printf("    th_read(): GNU long filename detected "
-		       "(%ld bytes, %d blocks)\n", sz, j);
+		       "(%ld bytes, %d blocks)\n", sz, blocks);
 #endif
-		t->th_buf.gnu_longname = (char *)malloc(j * T_BLOCKSIZE);
+		t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
 		if (t->th_buf.gnu_longname == NULL)
 			return -1;
 
-		for (ptr = t->th_buf.gnu_longname; j > 0;
-		     j--, ptr += T_BLOCKSIZE)
+		for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks;
+		     j++, ptr += T_BLOCKSIZE)
 		{
 #ifdef DEBUG
 			printf("    th_read(): reading long filename "
-			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+			       "(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
 #endif
 			i = tar_block_read(t, ptr);
 			if (i != T_BLOCKSIZE)
@@ -263,41 +283,6 @@
 	}
 #endif
 
-#if 0
-	/*
-	** work-around for old archive files with broken typeflag fields
-	** NOTE: I fixed this in the TH_IS*() macros instead
-	*/
-
-	/*
-	** (directories are signified with a trailing '/')
-	*/
-	if (t->th_buf.typeflag == AREGTYPE
-	    && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
-		t->th_buf.typeflag = DIRTYPE;
-
-	/*
-	** fallback to using mode bits
-	*/
-	if (t->th_buf.typeflag == AREGTYPE)
-	{
-		mode = (mode_t)oct_to_int(t->th_buf.mode);
-
-		if (S_ISREG(mode))
-			t->th_buf.typeflag = REGTYPE;
-		else if (S_ISDIR(mode))
-			t->th_buf.typeflag = DIRTYPE;
-		else if (S_ISFIFO(mode))
-			t->th_buf.typeflag = FIFOTYPE;
-		else if (S_ISCHR(mode))
-			t->th_buf.typeflag = CHRTYPE;
-		else if (S_ISBLK(mode))
-			t->th_buf.typeflag = BLKTYPE;
-		else if (S_ISLNK(mode))
-			t->th_buf.typeflag = SYMTYPE;
-	}
-#endif
-
 	return 0;
 }
 
@@ -308,7 +293,7 @@
 {
 	int i, j;
 	char type2;
-	size_t sz, sz2;
+	uint64_t sz, sz2;
 	char *ptr;
 	char buf[T_BLOCKSIZE];
 
@@ -457,7 +442,7 @@
 		}
 
 		memset(buf, 0, T_BLOCKSIZE);
-		snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", sz, t->th_buf.selinux_context);
+		snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", (int)sz, t->th_buf.selinux_context);
 		i = tar_block_write(t, &buf);
 		if (i != T_BLOCKSIZE)
 		{
diff --git a/libtar/compat.h b/libtar/compat.h
index d086294..70ac2f4 100644
--- a/libtar/compat.h
+++ b/libtar/compat.h
@@ -12,10 +12,6 @@
 # include <libgen.h>
 #endif
 
-#ifdef HAVE_SELINUX
-#include "selinux/selinux.h"
-#endif
-
 
 #if defined(NEED_BASENAME) && !defined(HAVE_BASENAME)
 
diff --git a/libtar/decode.c b/libtar/decode.c
index 383306d..1a3d0ee 100644
--- a/libtar/decode.c
+++ b/libtar/decode.c
@@ -13,6 +13,7 @@
 #include <internal.h>
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/param.h>
 #include <pwd.h>
 #include <grp.h>
@@ -26,22 +27,30 @@
 char *
 th_get_pathname(TAR *t)
 {
-	char filename[MAXPATHLEN];
-
-	if (t->th_buf.gnu_longname) {
-		printf("returning gnu longname\n");
+	if (t->th_buf.gnu_longname)
 		return t->th_buf.gnu_longname;
-	}
 
-	if (t->th_buf.prefix[0] != '\0')
+	/* allocate the th_pathname buffer if not already */
+	if (t->th_pathname == NULL)
 	{
-		snprintf(filename, sizeof(filename), "%.155s/%.100s",
-			 t->th_buf.prefix, t->th_buf.name);
-		return strdup(filename);
+		t->th_pathname = malloc(MAXPATHLEN * sizeof(char));
+		if (t->th_pathname == NULL)
+			/* out of memory */
+			return NULL;
 	}
 
-	snprintf(filename, sizeof(filename), "%.100s", t->th_buf.name);
-	return strdup(filename);
+	if (t->th_buf.prefix[0] == '\0')
+	{
+		snprintf(t->th_pathname, MAXPATHLEN, "%.100s", t->th_buf.name);
+	}
+	else
+	{
+		snprintf(t->th_pathname, MAXPATHLEN, "%.155s/%.100s",
+			 t->th_buf.prefix, t->th_buf.name);
+	}
+
+	/* will be deallocated in tar_close() */
+	return t->th_pathname;
 }
 
 
@@ -51,9 +60,11 @@
 	int uid;
 	struct passwd *pw;
 
-	pw = getpwnam(t->th_buf.uname);
-	if (pw != NULL)
-		return pw->pw_uid;
+	if (!(t->options & TAR_USE_NUMERIC_ID)) {
+		pw = getpwnam(t->th_buf.uname);
+		if (pw != NULL)
+			return pw->pw_uid;
+	}
 
 	/* if the password entry doesn't exist */
 	sscanf(t->th_buf.uid, "%o", &uid);
@@ -67,9 +78,11 @@
 	int gid;
 	struct group *gr;
 
-	gr = getgrnam(t->th_buf.gname);
-	if (gr != NULL)
-		return gr->gr_gid;
+	if (!(t->options & TAR_USE_NUMERIC_ID)) {
+		gr = getgrnam(t->th_buf.gname);
+		if (gr != NULL)
+			return gr->gr_gid;
+	}
 
 	/* if the group entry doesn't exist */
 	sscanf(t->th_buf.gid, "%o", &gid);
@@ -82,7 +95,7 @@
 {
 	mode_t mode;
 
-	mode = (mode_t)oct_to_int(t->th_buf.mode);
+	mode = (mode_t)oct_to_int(t->th_buf.mode, sizeof(t->th_buf.mode));
 	if (! (mode & S_IFMT))
 	{
 		switch (t->th_buf.typeflag)
@@ -103,7 +116,7 @@
 			mode |= S_IFIFO;
 			break;
 		case AREGTYPE:
-			if (t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
+			if (t->th_buf.name[strnlen(t->th_buf.name, T_NAMELEN) - 1] == '/')
 			{
 				mode |= S_IFDIR;
 				break;
diff --git a/libtar/dirname.c b/libtar/dirname.c
index 986db4a..4e06067 100644
--- a/libtar/dirname.c
+++ b/libtar/dirname.c
@@ -67,7 +67,7 @@
 		} while (endp > path && *endp == '/');
 	}
 
-	if (endp - path + 1 > sizeof(bname)) {
+	if (endp - path + 1 > (int)sizeof(bname)) {
 		errno = ENAMETOOLONG;
 		return(NULL);
 	}
diff --git a/libtar/encode.c b/libtar/encode.c
index 662eff5..c937152 100644
--- a/libtar/encode.c
+++ b/libtar/encode.c
@@ -68,10 +68,11 @@
 
 /* encode file path */
 void
-th_set_path(TAR *t, char *pathname)
+th_set_path(TAR *t, const char *pathname)
 {
 	char suffix[2] = "";
 	char *tmp;
+	size_t pathname_len = strlen(pathname);
 
 #ifdef DEBUG
 	printf("in th_set_path(th, pathname=\"%s\")\n", pathname);
@@ -81,32 +82,49 @@
 		free(t->th_buf.gnu_longname);
 	t->th_buf.gnu_longname = NULL;
 
-	if (pathname[strlen(pathname) - 1] != '/' && TH_ISDIR(t))
+	/* old archive compatibility (not needed for gnu): add trailing / to directories */
+	if (pathname[pathname_len - 1] != '/' && TH_ISDIR(t))
 		strcpy(suffix, "/");
 
-	if (strlen(pathname) > T_NAMELEN-1 && (t->options & TAR_GNU))
+	if (pathname_len >= T_NAMELEN && (t->options & TAR_GNU))
 	{
-		/* GNU-style long name */
+		/* GNU-style long name (no file name length limit) */
 		t->th_buf.gnu_longname = strdup(pathname);
 		strncpy(t->th_buf.name, t->th_buf.gnu_longname, T_NAMELEN);
 	}
-	else if (strlen(pathname) > T_NAMELEN)
+	else if (pathname_len >= T_NAMELEN)
 	{
-		/* POSIX-style prefix field */
-		tmp = strchr(&(pathname[strlen(pathname) - T_NAMELEN - 1]), '/');
+		/* POSIX-style prefix field:
+		 *   The maximum length of a file name is limited to 256 characters,
+		 *   provided that the file name can be split at a directory separator
+		 *   in two parts.  The first part being at most 155 bytes long and
+		 *   the second part being at most 100 bytes long.  So, in most cases
+		 *   the maximum file name length will be shorter than 256 characters.
+		 */
+		char tail_path[T_NAMELEN + 1];
+		tmp = strchr(&(pathname[pathname_len - T_NAMELEN]), '/');
 		if (tmp == NULL)
 		{
 			printf("!!! '/' not found in \"%s\"\n", pathname);
 			return;
 		}
-		snprintf(t->th_buf.name, 100, "%s%s", &(tmp[1]), suffix);
-		snprintf(t->th_buf.prefix,
-			 ((tmp - pathname + 1) <
-			  155 ? (tmp - pathname + 1) : 155), "%s", pathname);
+		snprintf(tail_path, T_NAMELEN + 1, "%s%s", &tmp[1], suffix);
+		strncpy(t->th_buf.name, tail_path, T_NAMELEN);
+
+		/*
+		 * first part, max = 155 == sizeof(t->th_buf.prefix) , include NULL if it fits
+		 * trailing '/' is added during decode: decode.c/th_get_pathname()
+		 */
+		if (tmp - pathname >= 155) {
+			strncpy(t->th_buf.prefix, pathname, 155);
+		} else {
+			snprintf(t->th_buf.prefix, (tmp - pathname + 1), "%s", pathname);
+		}
 	}
-	else
-		/* classic tar format */
-		snprintf(t->th_buf.name, 100, "%s%s", pathname, suffix);
+	else {
+		/* any short name for all formats, or classic tar format (99 chars max) */
+		snprintf(t->th_buf.name, T_NAMELEN, "%s%s", pathname, suffix);
+	}
 
 #ifdef DEBUG
 	puts("returning from th_set_path()...");
@@ -116,23 +134,28 @@
 
 /* encode link path */
 void
-th_set_link(TAR *t, char *linkname)
+th_set_link(TAR *t, const char *linkname)
 {
 #ifdef DEBUG
 	printf("==> th_set_link(th, linkname=\"%s\")\n", linkname);
 #endif
 
-	if (strlen(linkname) > T_NAMELEN-1 && (t->options & TAR_GNU))
+	if (strlen(linkname) >= T_NAMELEN && (t->options & TAR_GNU))
 	{
-		/* GNU longlink format */
+		/* --format=gnu: GNU-style long name (no file name length limit) */
 		t->th_buf.gnu_longlink = strdup(linkname);
 		strcpy(t->th_buf.linkname, "././@LongLink");
 	}
-	else
+	else if (strlen(linkname) >= T_NAMELEN)
 	{
-		/* classic tar format */
-		strlcpy(t->th_buf.linkname, linkname,
-			sizeof(t->th_buf.linkname));
+		/* --format=ustar: 100 chars max limit for symbolic links */
+		strncpy(t->th_buf.linkname, linkname, T_NAMELEN);
+		if (t->th_buf.gnu_longlink != NULL)
+			free(t->th_buf.gnu_longlink);
+		t->th_buf.gnu_longlink = NULL;
+	} else {
+		/* all short links or v7 tar format: The maximum length of a symbolic link name is limited to 99 characters */
+		snprintf(t->th_buf.linkname, T_NAMELEN, "%s", linkname);
 		if (t->th_buf.gnu_longlink != NULL)
 			free(t->th_buf.gnu_longlink);
 		t->th_buf.gnu_longlink = NULL;
@@ -159,9 +182,11 @@
 {
 	struct passwd *pw;
 
-	pw = getpwuid(uid);
-	if (pw != NULL)
-		strlcpy(t->th_buf.uname, pw->pw_name, sizeof(t->th_buf.uname));
+	if (!(t->options & TAR_USE_NUMERIC_ID)) {
+		pw = getpwuid(uid);
+		if (pw != NULL)
+			strlcpy(t->th_buf.uname, pw->pw_name, sizeof(t->th_buf.uname));
+	}
 
 	int_to_oct(uid, t->th_buf.uid, 8);
 }
@@ -173,9 +198,11 @@
 {
 	struct group *gr;
 
-	gr = getgrgid(gid);
-	if (gr != NULL)
-		strlcpy(t->th_buf.gname, gr->gr_name, sizeof(t->th_buf.gname));
+	if (!(t->options & TAR_USE_NUMERIC_ID)) {
+		gr = getgrgid(gid);
+		if (gr != NULL)
+			strlcpy(t->th_buf.gname, gr->gr_name, sizeof(t->th_buf.gname));
+	}
 
 	int_to_oct(gid, t->th_buf.gid, 8);
 }
@@ -209,5 +236,3 @@
 	else
 		th_set_size(t, 0);
 }
-
-
diff --git a/libtar/extract.c b/libtar/extract.c
index 69e08bd..257e140 100644
--- a/libtar/extract.c
+++ b/libtar/extract.c
@@ -13,14 +13,13 @@
 #include <internal.h>
 
 #include <stdio.h>
+#include <string.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <utime.h>
-#include <string.h>
 
-#define DEBUG
 #ifdef STDC_HEADERS
 # include <stdlib.h>
 #endif
@@ -29,26 +28,30 @@
 # include <unistd.h>
 #endif
 
-#define DEBUG
+#ifdef HAVE_SELINUX
+# include "selinux/selinux.h"
+#endif
 
 static int
-tar_set_file_perms(TAR *t, char *realname)
+tar_set_file_perms(TAR *t, const char *realname)
 {
 	mode_t mode;
 	uid_t uid;
 	gid_t gid;
 	struct utimbuf ut;
-	char *filename;
+	const char *filename;
+	char *pn;
 
-	filename = (realname ? realname : th_get_pathname(t));
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
 	mode = th_get_mode(t);
 	uid = th_get_uid(t);
 	gid = th_get_gid(t);
 	ut.modtime = ut.actime = th_get_mtime(t);
 
 #ifdef DEBUG
-	printf("   ==> setting perms: %s (mode %04o, uid %d, gid %d)\n",
-	       filename, mode, uid, gid);
+	printf("tar_set_file_perms(): setting perms: %s (mode %04o, uid %d, gid %d)\n",
+		filename, mode, uid, gid);
 #endif
 
 	/* change owner/group */
@@ -95,12 +98,15 @@
 
 /* switchboard */
 int
-tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd)
+tar_extract_file(TAR *t, const char *realname, const char *prefix, const int *progress_fd)
 {
 	int i;
+#ifdef LIBTAR_FILE_HASH
 	char *lnp;
+	char *pn;
 	int pathname_len;
 	int realname_len;
+#endif
 
 	if (t->options & TAR_NOOVERWRITE)
 	{
@@ -115,44 +121,31 @@
 
 	if (TH_ISDIR(t))
 	{
-		printf("dir\n");
 		i = tar_extract_dir(t, realname);
 		if (i == 1)
 			i = 0;
 	}
-	else if (TH_ISLNK(t)) {
-		printf("link\n");
+	else if (TH_ISLNK(t))
 		i = tar_extract_hardlink(t, realname, prefix);
-	}
-	else if (TH_ISSYM(t)) {
-		printf("sym\n");
+	else if (TH_ISSYM(t))
 		i = tar_extract_symlink(t, realname);
-	}
-	else if (TH_ISCHR(t)) {
-		printf("chr\n");
+	else if (TH_ISCHR(t))
 		i = tar_extract_chardev(t, realname);
-	}
-	else if (TH_ISBLK(t)) {
-		printf("blk\n");
+	else if (TH_ISBLK(t))
 		i = tar_extract_blockdev(t, realname);
-	}
-	else if (TH_ISFIFO(t)) {
-		printf("fifo\n");
+	else if (TH_ISFIFO(t))
 		i = tar_extract_fifo(t, realname);
-	}
-	else /* if (TH_ISREG(t)) */ {
-		printf("reg\n");
+	else /* if (TH_ISREG(t)) */
 		i = tar_extract_regfile(t, realname, progress_fd);
-	}
 
 	if (i != 0) {
-		printf("FAILED RESTORE OF FILE i: %s\n", realname);
+		fprintf(stderr, "tar_extract_file(): failed to extract %s !!!\n", realname);
 		return i;
 	}
 
 	i = tar_set_file_perms(t, realname);
 	if (i != 0) {
-		printf("FAILED SETTING PERMS: %d\n", i);
+		fprintf(stderr, "tar_extract_file(): failed to set permissions on %s !!!\n", realname);
 		return i;
 	}
 
@@ -160,51 +153,48 @@
 	if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
 	{
 #ifdef DEBUG
-		printf("   Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
+		printf("tar_extract_file(): restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
 #endif
-		if (lsetfilecon(realname, t->th_buf.selinux_context) < 0) {
-			fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
-		}
+		if (lsetfilecon(realname, t->th_buf.selinux_context) < 0)
+			fprintf(stderr, "tar_extract_file(): failed to restore SELinux context %s to file %s !!!\n", t->th_buf.selinux_context, realname);
 	}
 #endif
 
-/*
-	pathname_len = strlen(th_get_pathname(t)) + 1;
+#ifdef LIBTAR_FILE_HASH
+	pn = th_get_pathname(t);
+	pathname_len = strlen(pn) + 1;
 	realname_len = strlen(realname) + 1;
 	lnp = (char *)calloc(1, pathname_len + realname_len);
 	if (lnp == NULL)
 		return -1;
-	strcpy(&lnp[0], th_get_pathname(t));
+	strcpy(&lnp[0], pn);
 	strcpy(&lnp[pathname_len], realname);
 #ifdef DEBUG
 	printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
-	       "value=\"%s\"\n", th_get_pathname(t), realname);
+	       "value=\"%s\"\n", pn, realname);
 #endif
 	if (libtar_hash_add(t->h, lnp) != 0)
 		return -1;
 	free(lnp);
-*/
+#endif
+
 	return 0;
 }
 
 
 /* extract regular file */
 int
-tar_extract_regfile(TAR *t, char *realname, const int *progress_fd)
+tar_extract_regfile(TAR *t, const char *realname, const int *progress_fd)
 {
-	//mode_t mode;
-	size_t size, i;
-	//uid_t uid;
-	//gid_t gid;
+	int64_t size, i;
+	ssize_t k;
 	int fdout;
-	int k;
 	char buf[T_BLOCKSIZE];
-	char *filename;
+	const char *filename;
+	char *pn;
 
-	fflush(NULL);
 #ifdef DEBUG
-	printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
-	       realname);
+	printf("  ==> tar_extract_regfile(realname=\"%s\")\n", realname);
 #endif
 
 	if (!TH_ISREG(t))
@@ -213,21 +203,16 @@
 		return -1;
 	}
 
-	filename = (realname ? realname : th_get_pathname(t));
-	//mode = th_get_mode(t);
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
 	size = th_get_size(t);
-	//uid = th_get_uid(t);
-	//gid = th_get_gid(t);
 
 	if (mkdirhier(dirname(filename)) == -1)
 		return -1;
 
-#ifdef DEBUG
-	//printf("  ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
-	//       filename, mode, uid, gid, size);
-	printf("  ==> extracting: %s (file size %d bytes)\n",
-	       filename, size);
-#endif
+	printf("  ==> extracting: %s (file size %lld bytes)\n",
+			filename, size);
+
 	fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
 #ifdef O_BINARY
 		     | O_BINARY
@@ -241,41 +226,25 @@
 		return -1;
 	}
 
-#if 0
-	/* change the owner.  (will only work if run as root) */
-	if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
-	{
-#ifdef DEBUG
-		perror("fchown()");
-#endif
-		return -1;
-	}
-
-	/* make sure the mode isn't inheritted from a file we're overwriting */
-	if (fchmod(fdout, mode & 07777) == -1)
-	{
-#ifdef DEBUG
-		perror("fchmod()");
-#endif
-		return -1;
-	}
-#endif
-
 	/* extract the file */
-	for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE))
+	for (i = size; i > 0; i -= T_BLOCKSIZE)
 	{
 		k = tar_block_read(t, buf);
 		if (k != T_BLOCKSIZE)
 		{
 			if (k != -1)
 				errno = EINVAL;
+			close(fdout);
 			return -1;
 		}
 
 		/* write block to output file */
 		if (write(fdout, buf,
 			  ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
+		{
+			close(fdout);
 			return -1;
+		}
 	}
 
 	/* close output file */
@@ -286,7 +255,8 @@
 	printf("### done extracting %s\n", filename);
 #endif
 
-	if (*progress_fd != 0) {
+	if (*progress_fd != 0)
+	{
 		unsigned long long file_size = (unsigned long long)(size);
 		write(*progress_fd, &file_size, sizeof(file_size));
 	}
@@ -299,8 +269,8 @@
 int
 tar_skip_regfile(TAR *t)
 {
-	int k;
-	size_t size, i;
+	int64_t size, i;
+	ssize_t k;
 	char buf[T_BLOCKSIZE];
 
 	if (!TH_ISREG(t))
@@ -310,7 +280,7 @@
 	}
 
 	size = th_get_size(t);
-	for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE))
+	for (i = size; i > 0; i -= T_BLOCKSIZE)
 	{
 		k = tar_block_read(t, buf);
 		if (k != T_BLOCKSIZE)
@@ -327,10 +297,12 @@
 
 /* hardlink */
 int
-tar_extract_hardlink(TAR * t, char *realname, char *prefix)
+tar_extract_hardlink(TAR * t, const char *realname, const char *prefix)
 {
-	char *filename;
+	const char *filename;
+	char *pn;
 	char *linktgt = NULL;
+	char *newtgt = NULL;
 	char *lnp;
 	libtar_hashptr_t hp;
 
@@ -340,9 +312,12 @@
 		return -1;
 	}
 
-	filename = (realname ? realname : th_get_pathname(t));
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
 	if (mkdirhier(dirname(filename)) == -1)
 		return -1;
+	if (unlink(filename) == -1 && errno != ENOENT)
+		return -1;
 	libtar_hashptr_reset(&hp);
 	if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
 			       (libtar_matchfunc_t)libtar_str_match) != 0)
@@ -352,17 +327,15 @@
 	}
 	else
 		linktgt = th_get_linkname(t);
-	char *newtgt = strdup(linktgt);
+
+	newtgt = strdup(linktgt);
 	sprintf(linktgt, "%s/%s", prefix, newtgt);
-#ifdef DEBUG
+
 	printf("  ==> extracting: %s (link to %s)\n", filename, linktgt);
-#endif
+
 	if (link(linktgt, filename) == -1)
 	{
-#ifdef DEBUG
-		perror("link()");
-#endif
-		printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
+		fprintf(stderr, "tar_extract_hardlink(): failed restore of hardlink '%s' but returning as if nothing bad happened\n", filename);
 		return 0; // Used to be -1
 	}
 
@@ -372,33 +345,28 @@
 
 /* symlink */
 int
-tar_extract_symlink(TAR *t, char *realname)
+tar_extract_symlink(TAR *t, const char *realname)
 {
-	char *filename;
+	const char *filename;
+	char *pn;
 
 	if (!TH_ISSYM(t))
 	{
-		printf("not a sym\n");
 		errno = EINVAL;
 		return -1;
 	}
 
-	filename = (realname ? realname : th_get_pathname(t));
-	printf("file: %s\n", filename);
-	if (mkdirhier(dirname(filename)) == -1) {
-		printf("mkdirhier\n");
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
+	if (mkdirhier(dirname(filename)) == -1)
 		return -1;
-	}
 
-	if (unlink(filename) == -1 && errno != ENOENT) {
-		printf("unlink\n");
+	if (unlink(filename) == -1 && errno != ENOENT)
 		return -1;
-	}
 
-#ifdef DEBUG
 	printf("  ==> extracting: %s (symlink to %s)\n",
 	       filename, th_get_linkname(t));
-#endif
+
 	if (symlink(th_get_linkname(t), filename) == -1)
 	{
 #ifdef DEBUG
@@ -413,11 +381,12 @@
 
 /* character device */
 int
-tar_extract_chardev(TAR *t, char *realname)
+tar_extract_chardev(TAR *t, const char *realname)
 {
 	mode_t mode;
 	unsigned long devmaj, devmin;
-	char *filename;
+	const char *filename;
+	char *pn;
 
 	if (!TH_ISCHR(t))
 	{
@@ -425,7 +394,8 @@
 		return -1;
 	}
 
-	filename = (realname ? realname : th_get_pathname(t));
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
 	mode = th_get_mode(t);
 	devmaj = th_get_devmajor(t);
 	devmin = th_get_devminor(t);
@@ -433,17 +403,14 @@
 	if (mkdirhier(dirname(filename)) == -1)
 		return -1;
 
-#ifdef DEBUG
 	printf("  ==> extracting: %s (character device %ld,%ld)\n",
 	       filename, devmaj, devmin);
-#endif
+
 	if (mknod(filename, mode | S_IFCHR,
 		  compat_makedev(devmaj, devmin)) == -1)
 	{
-#ifdef DEBUG
-		printf("mknod() failed, returning good anyway");
-#endif
-		return 0;
+		fprintf(stderr, "tar_extract_chardev(): failed restore of character device '%s' but returning as if nothing bad happened\n", filename);
+		return 0; // Used to be -1
 	}
 
 	return 0;
@@ -452,11 +419,12 @@
 
 /* block device */
 int
-tar_extract_blockdev(TAR *t, char *realname)
+tar_extract_blockdev(TAR *t, const char *realname)
 {
 	mode_t mode;
 	unsigned long devmaj, devmin;
-	char *filename;
+	const char *filename;
+	char *pn;
 
 	if (!TH_ISBLK(t))
 	{
@@ -464,7 +432,8 @@
 		return -1;
 	}
 
-	filename = (realname ? realname : th_get_pathname(t));
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
 	mode = th_get_mode(t);
 	devmaj = th_get_devmajor(t);
 	devmin = th_get_devminor(t);
@@ -472,17 +441,14 @@
 	if (mkdirhier(dirname(filename)) == -1)
 		return -1;
 
-#ifdef DEBUG
 	printf("  ==> extracting: %s (block device %ld,%ld)\n",
 	       filename, devmaj, devmin);
-#endif
+
 	if (mknod(filename, mode | S_IFBLK,
 		  compat_makedev(devmaj, devmin)) == -1)
 	{
-#ifdef DEBUG
-		printf("mknod() failed but returning anyway");
-#endif
-		return 0;
+		fprintf(stderr, "tar_extract_blockdev(): failed restore of block device '%s' but returning as if nothing bad happened\n", filename);
+		return 0; // Used to be -1
 	}
 
 	return 0;
@@ -491,35 +457,45 @@
 
 /* directory */
 int
-tar_extract_dir(TAR *t, char *realname)
+tar_extract_dir(TAR *t, const char *realname)
 {
 	mode_t mode;
-	char *filename;
+	const char *filename;
+	char *pn;
+
 	if (!TH_ISDIR(t))
 	{
 		errno = EINVAL;
 		return -1;
 	}
-
-	filename = (realname ? realname : th_get_pathname(t));
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
 	mode = th_get_mode(t);
 
-	if (mkdirhier(dirname(filename)) == -1) {
-		printf("tar_extract_dir mkdirhier failed\n");
+	if (mkdirhier(dirname(filename)) == -1)
 		return -1;
-	}
 
-#ifdef DEBUG
 	printf("  ==> extracting: %s (mode %04o, directory)\n", filename,
 	       mode);
-#endif
+
 	if (mkdir(filename, mode) == -1)
 	{
 		if (errno == EEXIST)
 		{
+			if (chmod(filename, mode) == -1)
+			{
 #ifdef DEBUG
-			printf("  *** using existing directory");
+				perror("chmod()");
 #endif
+				return -1;
+			}
+			else
+			{
+#ifdef DEBUG
+				puts("  *** using existing directory");
+#endif
+				return 1;
+			}
 		}
 		else
 		{
@@ -536,10 +512,11 @@
 
 /* FIFO */
 int
-tar_extract_fifo(TAR *t, char *realname)
+tar_extract_fifo(TAR *t, const char *realname)
 {
 	mode_t mode;
-	char *filename;
+	const char *filename;
+	char *pn;
 
 	if (!TH_ISFIFO(t))
 	{
@@ -547,15 +524,16 @@
 		return -1;
 	}
 
-	filename = (realname ? realname : th_get_pathname(t));
+	pn = th_get_pathname(t);
+	filename = (realname ? realname : pn);
 	mode = th_get_mode(t);
 
 	if (mkdirhier(dirname(filename)) == -1)
 		return -1;
 
-#ifdef DEBUG
+
 	printf("  ==> extracting: %s (fifo)\n", filename);
-#endif
+
 	if (mkfifo(filename, mode) == -1)
 	{
 #ifdef DEBUG
@@ -567,4 +545,57 @@
 	return 0;
 }
 
+/* extract file contents from a tarchive */
+int
+tar_extract_file_contents(TAR *t, void *buf, size_t *lenp)
+{
+	char block[T_BLOCKSIZE];
+	int64_t size, i;
+	ssize_t k;
 
+#ifdef DEBUG
+	printf("  ==> tar_extract_file_contents\n");
+#endif
+
+	if (!TH_ISREG(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	size = th_get_size(t);
+	if ((uint64_t)size > *lenp)
+	{
+		errno = ENOSPC;
+		return -1;
+	}
+
+	/* extract the file */
+	for (i = size; i >= T_BLOCKSIZE; i -= T_BLOCKSIZE)
+	{
+		k = tar_block_read(t, buf);
+		if (k != T_BLOCKSIZE)
+		{
+			if (k != -1)
+				errno = EINVAL;
+			return -1;
+		}
+		buf = (char *)buf + T_BLOCKSIZE;
+	}
+	if (i > 0) {
+		k = tar_block_read(t, block);
+		if (k != T_BLOCKSIZE)
+		{
+			if (k != -1)
+				errno = EINVAL;
+			return -1;
+		}
+		memcpy(buf, block, i);
+	}
+	*lenp = (size_t)size;
+
+#ifdef DEBUG
+	printf("### done extracting contents\n");
+#endif
+	return 0;
+}
diff --git a/libtar/handle.c b/libtar/handle.c
index ae974b9..28a7dc2 100644
--- a/libtar/handle.c
+++ b/libtar/handle.c
@@ -31,7 +31,7 @@
 
 
 static int
-tar_init(TAR **t, char *pathname, tartype_t *type,
+tar_init(TAR **t, const char *pathname, tartype_t *type,
 	 int oflags, int mode, int options)
 {
 	if ((oflags & O_ACCMODE) == O_RDWR)
@@ -66,7 +66,7 @@
 
 /* open a new tarfile handle */
 int
-tar_open(TAR **t, char *pathname, tartype_t *type,
+tar_open(TAR **t, const char *pathname, tartype_t *type,
 	 int oflags, int mode, int options)
 {
 	if (tar_init(t, pathname, type, oflags, mode, options) == -1)
@@ -82,6 +82,7 @@
 	(*t)->fd = (*((*t)->type->openfunc))(pathname, oflags, mode);
 	if ((*t)->fd == -1)
 	{
+		libtar_hash_free((*t)->h, NULL);
 		free(*t);
 		return -1;
 	}
@@ -91,7 +92,7 @@
 
 
 int
-tar_fdopen(TAR **t, int fd, char *pathname, tartype_t *type,
+tar_fdopen(TAR **t, int fd, const char *pathname, tartype_t *type,
 	   int oflags, int mode, int options)
 {
 	if (tar_init(t, pathname, type, oflags, mode, options) == -1)
@@ -121,6 +122,8 @@
 		libtar_hash_free(t->h, ((t->oflags & O_ACCMODE) == O_RDONLY
 					? free
 					: (libtar_freefunc_t)tar_dev_free));
+	if (t->th_pathname != NULL)
+		free(t->th_pathname);
 	free(t);
 
 	return i;
diff --git a/libtar/libtar.h b/libtar/libtar.h
index d2c4d00..4a51375 100644
--- a/libtar/libtar.h
+++ b/libtar/libtar.h
@@ -26,6 +26,7 @@
 
 
 /* useful constants */
+/* see FIXME note in block.c regarding T_BLOCKSIZE */
 #define T_BLOCKSIZE		512
 #define T_NAMELEN		100
 #define T_PREFIXLEN		155
@@ -85,12 +86,15 @@
 typedef struct
 {
 	tartype_t *type;
-	char *pathname;
+	const char *pathname;
 	long fd;
 	int oflags;
 	int options;
 	struct tar_header th_buf;
 	libtar_hash_t *h;
+
+	/* introduced in libtar 1.2.21 */
+	char *th_pathname;
 }
 TAR;
 
@@ -103,6 +107,7 @@
 #define TAR_CHECK_VERSION	32	/* check version in file header */
 #define TAR_IGNORE_CRC		64	/* ignore CRC in file header */
 #define TAR_STORE_SELINUX	128	/* store selinux context */
+#define TAR_USE_NUMERIC_ID	256	/* favor numeric owner over names */
 
 /* this is obsolete - it's here for backwards-compatibility only */
 #define TAR_IGNORE_MAGIC	0
@@ -111,11 +116,11 @@
 
 
 /* open a new tarfile handle */
-int tar_open(TAR **t, char *pathname, tartype_t *type,
+int tar_open(TAR **t, const char *pathname, tartype_t *type,
 	     int oflags, int mode, int options);
 
 /* make a tarfile handle out of a previously-opened descriptor */
-int tar_fdopen(TAR **t, int fd, char *pathname, tartype_t *type,
+int tar_fdopen(TAR **t, int fd, const char *pathname, tartype_t *type,
 	       int oflags, int mode, int options);
 
 /* returns the descriptor associated with t */
@@ -139,14 +144,27 @@
  *    realname = path of file to append
  *    savename = name to save the file under in the archive
  */
-int tar_append_file(TAR *t, char *realname, char *savename);
+int tar_append_file(TAR *t, const char *realname, const char *savename);
 
 /* write EOF indicator */
 int tar_append_eof(TAR *t);
 
 /* add file contents to a tarchive */
-int tar_append_regfile(TAR *t, char *realname);
+int tar_append_regfile(TAR *t, const char *realname);
 
+/* Appends in-memory file contents to a tarchive.
+ * Arguments:
+ *    t        = TAR handle to append to
+ *    savename = name to save the file under in the archive
+ *    mode     = mode
+ *    uid, gid = owner
+ *    buf, len = in-memory buffer
+ */
+int tar_append_file_contents(TAR *t, const char *savename, mode_t mode,
+                             uid_t uid, gid_t gid, void *buf, size_t len);
+
+/* add buffer to a tarchive */
+int tar_append_buffer(TAR *t, void *buf, size_t len);
 
 /***** block.c *************************************************************/
 
@@ -167,31 +185,32 @@
 #define TH_ISREG(t)	((t)->th_buf.typeflag == REGTYPE \
 			 || (t)->th_buf.typeflag == AREGTYPE \
 			 || (t)->th_buf.typeflag == CONTTYPE \
-			 || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode)) \
+			 || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))) \
 			     && (t)->th_buf.typeflag != LNKTYPE))
 #define TH_ISLNK(t)	((t)->th_buf.typeflag == LNKTYPE)
 #define TH_ISSYM(t)	((t)->th_buf.typeflag == SYMTYPE \
-			 || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode)))
+			 || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
 #define TH_ISCHR(t)	((t)->th_buf.typeflag == CHRTYPE \
-			 || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode)))
+			 || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
 #define TH_ISBLK(t)	((t)->th_buf.typeflag == BLKTYPE \
-			 || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode)))
+			 || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
 #define TH_ISDIR(t)	((t)->th_buf.typeflag == DIRTYPE \
-			 || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode)) \
+			 || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))) \
 			 || ((t)->th_buf.typeflag == AREGTYPE \
-			     && ((t)->th_buf.name[strlen((t)->th_buf.name) - 1] == '/')))
+			     && strnlen((t)->th_buf.name, T_NAMELEN) \
+			     && ((t)->th_buf.name[strnlen((t)->th_buf.name, T_NAMELEN) - 1] == '/')))
 #define TH_ISFIFO(t)	((t)->th_buf.typeflag == FIFOTYPE \
-			 || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode)))
+			 || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode, sizeof((t)->th_buf.mode))))
 #define TH_ISLONGNAME(t)	((t)->th_buf.typeflag == GNU_LONGNAME_TYPE)
 #define TH_ISLONGLINK(t)	((t)->th_buf.typeflag == GNU_LONGLINK_TYPE)
 #define TH_ISEXTHEADER(t)	((t)->th_buf.typeflag == TH_EXT_TYPE)
 
 /* decode tar header info */
-#define th_get_crc(t) oct_to_int((t)->th_buf.chksum)
-#define th_get_size(t) oct_to_int((t)->th_buf.size)
-#define th_get_mtime(t) oct_to_int((t)->th_buf.mtime)
-#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor)
-#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor)
+#define th_get_crc(t) oct_to_int((t)->th_buf.chksum, sizeof((t)->th_buf.chksum))
+#define th_get_size(t) oct_to_int_ex((t)->th_buf.size, sizeof((t)->th_buf.size))
+#define th_get_mtime(t) oct_to_int_ex((t)->th_buf.mtime, sizeof((t)->th_buf.mtime))
+#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor, sizeof((t)->th_buf.devmajor))
+#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor, sizeof((t)->th_buf.devminor))
 #define th_get_linkname(t) ((t)->th_buf.gnu_longlink \
                             ? (t)->th_buf.gnu_longlink \
                             : (t)->th_buf.linkname)
@@ -205,16 +224,16 @@
 
 /* encode file info in th_header */
 void th_set_type(TAR *t, mode_t mode);
-void th_set_path(TAR *t, char *pathname);
-void th_set_link(TAR *t, char *linkname);
+void th_set_path(TAR *t, const char *pathname);
+void th_set_link(TAR *t, const char *linkname);
 void th_set_device(TAR *t, dev_t device);
 void th_set_user(TAR *t, uid_t uid);
 void th_set_group(TAR *t, gid_t gid);
 void th_set_mode(TAR *t, mode_t fmode);
 #define th_set_mtime(t, fmtime) \
-	int_to_oct_nonull((fmtime), (t)->th_buf.mtime, 12)
+	int_to_oct_ex((fmtime), (t)->th_buf.mtime, sizeof((t)->th_buf.mtime))
 #define th_set_size(t, fsize) \
-	int_to_oct_nonull((fsize), (t)->th_buf.size, 12)
+	int_to_oct_ex((fsize), (t)->th_buf.size, sizeof((t)->th_buf.size))
 
 /* encode everything at once (except the pathname and linkname) */
 void th_set_from_stat(TAR *t, struct stat *s);
@@ -226,20 +245,22 @@
 /***** extract.c ***********************************************************/
 
 /* sequentially extract next file from t */
-int tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd);
+int tar_extract_file(TAR *t, const char *realname, const char *prefix, const int *progress_fd);
 
 /* extract different file types */
-int tar_extract_dir(TAR *t, char *realname);
-int tar_extract_hardlink(TAR *t, char *realname, char *prefix);
-int tar_extract_symlink(TAR *t, char *realname);
-int tar_extract_chardev(TAR *t, char *realname);
-int tar_extract_blockdev(TAR *t, char *realname);
-int tar_extract_fifo(TAR *t, char *realname);
+int tar_extract_dir(TAR *t, const char *realname);
+int tar_extract_hardlink(TAR *t, const char *realname, const char *prefix);
+int tar_extract_symlink(TAR *t, const char *realname);
+int tar_extract_chardev(TAR *t, const char *realname);
+int tar_extract_blockdev(TAR *t, const char *realname);
+int tar_extract_fifo(TAR *t, const char *realname);
 
 /* for regfiles, we need to extract the content blocks as well */
-int tar_extract_regfile(TAR *t, char *realname, const int *progress_fd);
+int tar_extract_regfile(TAR *t, const char *realname, const int *progress_fd);
 int tar_skip_regfile(TAR *t);
 
+/* extract regfile to buffer */
+int tar_extract_file_contents(TAR *t, void *buf, size_t *lenp);
 
 /***** output.c ************************************************************/
 
@@ -280,16 +301,17 @@
 #define th_crc_ok(t) (th_get_crc(t) == th_crc_calc(t) || th_get_crc(t) == th_signed_crc_calc(t))
 
 /* string-octal to integer conversion */
-int oct_to_int(char *oct);
+int64_t oct_to_int(char *oct, size_t len);
+
+/* string-octal or binary to integer conversion */
+int64_t oct_to_int_ex(char *oct, size_t len);
 
 /* integer to NULL-terminated string-octal conversion */
-#define int_to_oct(num, oct, octlen) \
-	snprintf((oct), (octlen), "%*lo ", (octlen) - 2, (unsigned long)(num))
+void int_to_oct(int64_t num, char *oct, size_t octlen);
 
-/* integer to string-octal conversion, no NULL */
-void int_to_oct_nonull(int num, char *oct, size_t octlen);
+/* integer to string-octal conversion, or binary as necessary */
+void int_to_oct_ex(int64_t num, char *oct, size_t octlen);
 
-#define tar_min(x, y) (x < y ? x : y)
 
 /***** wrapper.c **********************************************************/
 
@@ -298,7 +320,7 @@
 int tar_extract_all(TAR *t, char *prefix, const int *progress_fd);
 
 /* add a whole tree of files */
-int tar_append_tree(TAR *t, char *realdir, char *savedir, char *exclude);
+int tar_append_tree(TAR *t, char *realdir, char *savedir);
 
 /* find an entry */
 int tar_find(TAR *t, char *searchstr);
diff --git a/libtar/libtar_listhash.h b/libtar/libtar_listhash.h
index fa33cfd..48c0d74 100644
--- a/libtar/libtar_listhash.h
+++ b/libtar/libtar_listhash.h
@@ -15,6 +15,9 @@
 #ifndef libtar_LISTHASH_H
 #define libtar_LISTHASH_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 /***** list.c **********************************************************/
 
@@ -192,5 +195,9 @@
 int libtar_hash_del(libtar_hash_t *,
 			       libtar_hashptr_t *);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* ! libtar_LISTHASH_H */
 
diff --git a/libtar/output.c b/libtar/output.c
index a2db929..d2bf8bb 100644
--- a/libtar/output.c
+++ b/libtar/output.c
@@ -13,6 +13,7 @@
 #include <internal.h>
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <pwd.h>
 #include <grp.h>
 #include <time.h>
@@ -83,14 +84,14 @@
 
 	uid = th_get_uid(t);
 	pw = getpwuid(uid);
-	if (pw == NULL)
+	if ((t->options & TAR_USE_NUMERIC_ID) || pw == NULL)
 		snprintf(username, sizeof(username), "%d", uid);
 	else
 		strlcpy(username, pw->pw_name, sizeof(username));
 
 	gid = th_get_gid(t);
 	gr = getgrgid(gid);
-	if (gr == NULL)
+	if ((t->options & TAR_USE_NUMERIC_ID) || gr == NULL)
 		snprintf(groupname, sizeof(groupname), "%d", gid);
 	else
 		strlcpy(groupname, gr->gr_name, sizeof(groupname));
@@ -99,7 +100,7 @@
 	printf("%.10s %-8.8s %-8.8s ", modestring, username, groupname);
 
 	if (TH_ISCHR(t) || TH_ISBLK(t))
-		printf(" %3d, %3d ", th_get_devmajor(t), th_get_devminor(t));
+		printf(" %3d, %3d ", (int)th_get_devmajor(t), (int)th_get_devminor(t));
 	else
 		printf("%9ld ", (long)th_get_size(t));
 
diff --git a/libtar/util.c b/libtar/util.c
index 31e8315..f472f38 100644
--- a/libtar/util.c
+++ b/libtar/util.c
@@ -126,7 +126,6 @@
 	return sum;
 }
 
-
 /* calculate a signed header checksum */
 int
 th_signed_crc_calc(TAR *t)
@@ -141,25 +140,73 @@
 	return sum;
 }
 
-
 /* string-octal to integer conversion */
-int
-oct_to_int(char *oct)
+int64_t
+oct_to_int(char *oct, size_t octlen)
 {
-	int i;
+	long long int val;
+	char tmp[octlen + 1];
 
-	sscanf(oct, "%o", &i);
-
-	return i;
+	memcpy(tmp, oct, octlen);
+	tmp[octlen] = '\0';
+	return sscanf(oct, "%llo", &val) == 1 ? (int64_t)val : 0;
 }
 
 
-/* integer to string-octal conversion, no NULL */
+/* string-octal or binary to integer conversion */
+int64_t oct_to_int_ex(char *oct, size_t octlen)
+{
+	if (*(unsigned char *)oct & 0x80) {
+		int64_t val = 0;
+		char tmp[octlen];
+		unsigned char *p;
+		unsigned int i;
+
+		memcpy(tmp, oct, octlen);
+		*tmp &= 0x7f;
+		p = (unsigned char *)tmp + octlen - sizeof(val);
+		for (i = 0; i < sizeof(val); ++i) {
+			val <<= 8;
+			val |= *(p++);
+		}
+		return val;
+	}
+	return oct_to_int(oct, octlen);
+}
+
+
+/* integer to NULL-terminated string-octal conversion */
+void int_to_oct(int64_t num, char *oct, size_t octlen)
+{
+	char tmp[sizeof(num)*3 + 1];
+	int olen;
+
+	olen = sprintf(tmp, "%0*llo", (int)octlen, (long long)num);
+	memcpy(oct, tmp + olen - octlen + 1, octlen);
+}
+
+
+/* integer to string-octal conversion, or binary as necessary */
 void
-int_to_oct_nonull(int num, char *oct, size_t octlen)
+int_to_oct_ex(int64_t num, char *oct, size_t octlen)
 {
-	snprintf(oct, octlen, "%*lo", octlen - 1, (unsigned long)num);
-	oct[octlen - 1] = ' ';
+	if (num < 0 || num >= ((int64_t)1 << ((octlen - 1) * 3))) {
+		unsigned char *p;
+		unsigned int i;
+
+		memset(oct, 0, octlen);
+		p = (unsigned char *)oct + octlen;
+		for (i = 0; i < sizeof(num); ++i) {
+			*(--p) = num & 0xff;
+			num >>= 8;
+		}
+		if (num < 0) {
+			for (; i < octlen; ++i) {
+				*(--p) = 0xff;
+			}
+		}
+		*(unsigned char *)oct |= 0x80;
+		return;
+	}
+	int_to_oct(num, oct, octlen);
 }
-
-
diff --git a/libtar/wrapper.c b/libtar/wrapper.c
index 82f045f..d3d285f 100644
--- a/libtar/wrapper.c
+++ b/libtar/wrapper.c
@@ -10,18 +10,19 @@
 **  University of Illinois at Urbana-Champaign
 */
 
-#define DEBUG
 #include <internal.h>
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/param.h>
 #include <dirent.h>
 #include <errno.h>
-#include <stdlib.h>
+
 #ifdef STDC_HEADERS
 # include <string.h>
 #endif
 
+
 int
 tar_extract_glob(TAR *t, char *globname, char *prefix)
 {
@@ -44,7 +45,7 @@
 			snprintf(buf, sizeof(buf), "%s/%s", prefix, filename);
 		else
 			strlcpy(buf, filename, sizeof(buf));
-		if (tar_extract_file(t, filename, prefix, &fd) != 0)
+		if (tar_extract_file(t, buf, prefix, &fd) != 0)
 			return -1;
 	}
 
@@ -58,11 +59,12 @@
 	char *filename;
 	char buf[MAXPATHLEN];
 	int i;
-	printf("prefix: %s\n", prefix);
+
 #ifdef DEBUG
 	printf("==> tar_extract_all(TAR *t, \"%s\")\n",
 	       (prefix ? prefix : "(null)"));
 #endif
+
 	while ((i = th_read(t)) == 0)
 	{
 #ifdef DEBUG
@@ -79,105 +81,71 @@
 		printf("    tar_extract_all(): calling tar_extract_file(t, "
 		       "\"%s\")\n", buf);
 #endif
-		printf("item name: '%s'\n", filename);
 		if (tar_extract_file(t, buf, prefix, progress_fd) != 0)
 			return -1;
 	}
+
 	return (i == 1 ? 0 : -1);
 }
 
 
 int
-tar_append_tree(TAR *t, char *realdir, char *savedir, char *exclude)
+tar_append_tree(TAR *t, char *realdir, char *savedir)
 {
-#ifdef DEBUG
-	printf("==> tar_append_tree(0x%lx, \"%s\", \"%s\")\n",
-	       (long unsigned int)t, realdir, (savedir ? savedir : "[NULL]"));
-#endif
-
-	char temp[1024];
-	int skip = 0, i, n_spaces = 0;
-	char ** excluded = NULL;
-	char * p = NULL;
-	if (exclude) {
-		strcpy(temp, exclude);
-		p = strtok(exclude, " ");
-		if (p == NULL) {
-			excluded = realloc(excluded, sizeof(char*) * (++n_spaces));
-			excluded[0] = temp;
-		} else {
-			while (p) {
-				excluded = realloc(excluded, sizeof(char*) * (++n_spaces));
-				excluded[n_spaces-1] = p;
-				p = strtok(NULL, " ");
-			}
-		}
-		excluded = realloc(excluded, sizeof(char*) * (n_spaces+1));
-		excluded[n_spaces] = 0;
-		for (i = 0; i < (n_spaces+1); i++) {
-			if (realdir == excluded[i]) {
-				printf("    excluding '%s'\n", excluded[i]);
-				skip = 1;
-				break;
-			}
-		}
-	}
-	if (skip == 0) {
-		if (tar_append_file(t, realdir, savedir) != 0)
-			return -1;
-	}
-
 	char realpath[MAXPATHLEN];
 	char savepath[MAXPATHLEN];
 	struct dirent *dent;
 	DIR *dp;
 	struct stat s;
 
+#ifdef DEBUG
+	printf("==> tar_append_tree(0x%lx, \"%s\", \"%s\")\n",
+	       t, realdir, (savedir ? savedir : "[NULL]"));
+#endif
+
+	if (tar_append_file(t, realdir, savedir) != 0)
+		return -1;
+
+#ifdef DEBUG
+	puts("    tar_append_tree(): done with tar_append_file()...");
+#endif
+
 	dp = opendir(realdir);
-	if (dp == NULL) {
+	if (dp == NULL)
+	{
 		if (errno == ENOTDIR)
 			return 0;
 		return -1;
 	}
-	while ((dent = readdir(dp)) != NULL) {
-		if(strcmp(dent->d_name, ".") == 0
-		|| strcmp(dent->d_name, "..") == 0)
+	while ((dent = readdir(dp)) != NULL)
+	{
+		if (strcmp(dent->d_name, ".") == 0 ||
+		    strcmp(dent->d_name, "..") == 0)
 			continue;
 
-		if (exclude) {
-			int omit = 0;
-			for (i = 0; i < (n_spaces+1); i++) {
-				if (excluded[i] != NULL) {
-						if (strcmp(dent->d_name, excluded[i]) == 0 || strcmp(excluded[i], realdir) == 0) {
-							printf("    excluding '%s'\n", excluded[i]);
-							omit = 1;
-							break;
-						}
-				}
-			}
-			if (omit)
-				continue;
-		}
-
-		snprintf(realpath, MAXPATHLEN, "%s/%s", realdir, dent->d_name);
+		snprintf(realpath, MAXPATHLEN, "%s/%s", realdir,
+			 dent->d_name);
 		if (savedir)
-			snprintf(savepath, MAXPATHLEN, "%s/%s", savedir, dent->d_name);
+			snprintf(savepath, MAXPATHLEN, "%s/%s", savedir,
+				 dent->d_name);
 
 		if (lstat(realpath, &s) != 0)
 			return -1;
 
-		if (S_ISDIR(s.st_mode)) {
-			if (tar_append_tree(t, realpath, (savedir ? savepath : NULL), (exclude ? exclude : NULL)) != 0)
-				return -1;
-			continue;
-		} else {
-			if (tar_append_file(t, realpath, (savedir ? savepath : NULL)) != 0)
+		if (S_ISDIR(s.st_mode))
+		{
+			if (tar_append_tree(t, realpath,
+					    (savedir ? savepath : NULL)) != 0)
 				return -1;
 			continue;
 		}
+
+		if (tar_append_file(t, realpath,
+				    (savedir ? savepath : NULL)) != 0)
+			return -1;
 	}
+
 	closedir(dp);
-	free(excluded);
 
 	return 0;
 }