tar: improve OLDGNU compat, make old SUN compat configurable

diff --git a/archival/Config.in b/archival/Config.in
index 661c71c..49070da 100644
--- a/archival/Config.in
+++ b/archival/Config.in
@@ -205,6 +205,15 @@
 	  the old GNU format; help to kill this old format by
 	  repacking your ancient archives with the new format.
 
+config FEATURE_TAR_OLDSUN_COMPATIBILITY
+	bool "Enable untarring of tarballs with checksums produced by buggy Sun tar"
+	default N
+	depends on TAR
+	help
+	  This option is required to unpack archives created by some old
+	  version of Sun's tar (it was calculating checksum using signed arithmetic).
+	  It is said to be fixed in newer Sun tar, but "old" tarballs still exist.
+
 config FEATURE_TAR_GNU_EXTENSIONS
 	bool "Enable support for some GNU tar extensions"
 	default y
diff --git a/archival/libunarchive/get_header_cpio.c b/archival/libunarchive/get_header_cpio.c
index 724368b..3f51355 100644
--- a/archival/libunarchive/get_header_cpio.c
+++ b/archival/libunarchive/get_header_cpio.c
@@ -16,8 +16,9 @@
 char get_header_cpio(archive_handle_t *archive_handle)
 {
 	static hardlinks_t *saved_hardlinks = NULL;
-	static unsigned short pending_hardlinks = 0;
+	static unsigned pending_hardlinks = 0;
 	static int inode;
+
 	file_header_t *file_header = archive_handle->file_header;
 	char cpio_header[110];
 	int namesize;
diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c
index d42f4c2..1a7a687 100644
--- a/archival/libunarchive/get_header_tar.c
+++ b/archival/libunarchive/get_header_tar.c
@@ -36,7 +36,7 @@
 	*/
 	str[len] = '\0';
 	v = strtoull(str, &str, 8);
-	if (*str)
+	if (*str && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || *str != ' '))
 		bb_error_msg_and_die("corrupted octal value in tar header");
 	return v;
 }
@@ -45,7 +45,7 @@
 void BUG_tar_header_size(void);
 char get_header_tar(archive_handle_t *archive_handle)
 {
-	static int end;
+	static smallint end;
 
 	file_header_t *file_header = archive_handle->file_header;
 	struct {
@@ -69,7 +69,10 @@
 		char padding[12];   /* 500-512 */
 	} tar;
 	char *cp;
-	int i, sum_u, sum_s, sum;
+	int i, sum_u, sum;
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
+	int sum_s;
+#endif
 	int parse_names;
 
 	if (sizeof(tar) != 512)
@@ -115,20 +118,36 @@
 	 * POSIX says that checksum is done on unsigned bytes, but
 	 * Sun and HP-UX gets it wrong... more details in
 	 * GNU tar source. */
-	sum_s = sum_u = ' ' * sizeof(tar.chksum);
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
+	sum_s = ' ' * sizeof(tar.chksum);
+#endif
+	sum_u = ' ' * sizeof(tar.chksum);
 	for (i = 0; i < 148 ; i++) {
 		sum_u += ((unsigned char*)&tar)[i];
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
 		sum_s += ((signed char*)&tar)[i];
+#endif
 	}
 	for (i = 156; i < 512 ; i++) {
 		sum_u += ((unsigned char*)&tar)[i];
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
 		sum_s += ((signed char*)&tar)[i];
+#endif
 	}
-	/* This field does not need special treatment (getOctal) */
-	sum = xstrtoul(tar.chksum, 8);
-	if (sum_u != sum && sum_s != sum) {
+#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
+	sum = strtoul(tar.chksum, &cp, 8);
+	if ((*cp && *cp != ' ')
+	 || (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum))
+	) {
 		bb_error_msg_and_die("invalid tar header checksum");
 	}
+#else
+	/* This field does not need special treatment (getOctal) */
+	sum = xstrtoul(tar.chksum, 8);
+	if (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) {
+		bb_error_msg_and_die("invalid tar header checksum");
+	}
+#endif
 
 	/* 0 is reserved for high perf file, treat as normal file */
 	if (!tar.typeflag) tar.typeflag = '0';