aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archival/libunarchive/get_header_tar.c73
-rw-r--r--archival/libunarchive/header_verbose_list.c4
-rw-r--r--archival/tar.c3
3 files changed, 76 insertions, 4 deletions
diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c
index bf0f92b25..48fc23d8b 100644
--- a/archival/libunarchive/get_header_tar.c
+++ b/archival/libunarchive/get_header_tar.c
@@ -14,6 +14,74 @@
#include "libbb.h"
#include "unarchive.h"
+/*
+ * GNU tar uses "base-256 encoding" for very large numbers (>8 billion).
+ * Encoding is binary, with highest bit always set as a marker
+ * and sign in next-highest bit:
+ * 80 00 .. 00 - zero
+ * bf ff .. ff - largest positive number
+ * ff ff .. ff - minus 1
+ * c0 00 .. 00 - smallest negative number
+ *
+ * We expect it only in size field, where negative numbers don't make sense.
+ */
+static off_t getBase256_len12(const char *str)
+{
+ off_t value;
+ int len;
+
+ /* if (*str & 0x40) error; - caller prevents this */
+
+ if (sizeof(off_t) >= 12) {
+ /* Probably 128-bit (16 byte) off_t. Can be optimized. */
+ len = 12;
+ value = *str++ & 0x3f;
+ while (--len)
+ value = (value << 8) + (unsigned char) *str++;
+ return value;
+ }
+
+#ifdef CHECK_FOR_OVERFLOW
+ /* Can be optimized to eat 32-bit chunks */
+ char c = *str++ & 0x3f;
+ len = 12;
+ while (1) {
+ if (c)
+ bb_error_msg_and_die("overflow in base-256 encoded file size");
+ if (--len == sizeof(off_t))
+ break;
+ c = *str++;
+ }
+#else
+ str += (12 - sizeof(off_t));
+#endif
+
+/* Now str points to sizeof(off_t) least significant bytes.
+ *
+ * Example of tar file with 8914993153 (0x213600001) byte file.
+ * Field starts at offset 7c:
+ * 00070 30 30 30 00 30 30 30 30 30 30 30 00 80 00 00 00 |000.0000000.....|
+ * 00080 00 00 00 02 13 60 00 01 31 31 31 32 30 33 33 36 |.....`..11120336|
+ *
+ * str is at offset 80 or 84 now (64-bit or 32-bit off_t).
+ * We (ab)use the fact that value happens to be aligned,
+ * and fetch it in one go:
+ */
+ if (sizeof(off_t) == 8) {
+ value = *(off_t*)str;
+ value = SWAP_BE64(value);
+ } else if (sizeof(off_t) == 4) {
+ value = *(off_t*)str;
+ value = SWAP_BE32(value);
+ } else {
+ value = 0;
+ len = sizeof(off_t);
+ while (--len)
+ value = (value << 8) + (unsigned char) *str++;
+ }
+ return value;
+}
+
/* NB: _DESTROYS_ str[len] character! */
static unsigned long long getOctal(char *str, int len)
{
@@ -234,7 +302,10 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
file_header->gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL;
#endif
file_header->mtime = GET_OCTAL(tar.mtime);
- file_header->size = GET_OCTAL(tar.size);
+ /* Size field: handle GNU tar's "base256 encoding" */
+ file_header->size = (*tar.size & 0xc0) == 0x80 /* positive base256? */
+ ? getBase256_len12(tar.size)
+ : GET_OCTAL(tar.size);
file_header->gid = GET_OCTAL(tar.gid);
file_header->uid = GET_OCTAL(tar.uid);
/* Set bits 0-11 of the files mode */
diff --git a/archival/libunarchive/header_verbose_list.c b/archival/libunarchive/header_verbose_list.c
index f059dd981..dc3100361 100644
--- a/archival/libunarchive/header_verbose_list.c
+++ b/archival/libunarchive/header_verbose_list.c
@@ -24,11 +24,11 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header)
snprintf(gid, sizeof(gid), "%u", (unsigned)file_header->gid);
group = gid;
}
- printf("%s %s/%s %9u %4u-%02u-%02u %02u:%02u:%02u %s",
+ printf("%s %s/%s %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s",
bb_mode_string(file_header->mode),
user,
group,
- (unsigned int) file_header->size,
+ file_header->size,
1900 + mtime->tm_year,
1 + mtime->tm_mon,
mtime->tm_mday,
diff --git a/archival/tar.c b/archival/tar.c
index deb5c89b0..76f1a6240 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -357,7 +357,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
if (tbInfo->verboseFlag) {
FILE *vbFd = stdout;
- if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */
+ /* If archive goes to stdout, verbose goes to stderr */
+ if (tbInfo->tarFd == STDOUT_FILENO)
vbFd = stderr;
/* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
/* We don't have such excesses here: for us "v" == "vv" */