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/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;
+}