aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-05-06 20:08:14 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-05-06 20:08:14 +0200
commit7b48eb4372c0b639bcb1eaedd63795c2dc0d9820 (patch)
treef22da6d2d3ce82aa6d2c51cac27ab793cac4750d
parentc7efd6441def53df0200d7cce24e2956ad96aa02 (diff)
downloadbusybox-w32-7b48eb4372c0b639bcb1eaedd63795c2dc0d9820.tar.gz
busybox-w32-7b48eb4372c0b639bcb1eaedd63795c2dc0d9820.tar.bz2
busybox-w32-7b48eb4372c0b639bcb1eaedd63795c2dc0d9820.zip
tar: support GNU 256-bit encoding in all numeric fields
function old new delta getOctal 63 125 +62 get_header_tar 1572 1496 -76 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 62/-76) Total: -14 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--archival/libunarchive/get_header_tar.c111
1 files changed, 31 insertions, 80 deletions
diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c
index adb4c157b..fcddcb834 100644
--- a/archival/libunarchive/get_header_tar.c
+++ b/archival/libunarchive/get_header_tar.c
@@ -18,87 +18,45 @@ typedef uint32_t aliased_uint32_t FIX_ALIASING;
18typedef off_t aliased_off_t FIX_ALIASING; 18typedef off_t aliased_off_t FIX_ALIASING;
19 19
20 20
21/*
22 * GNU tar uses "base-256 encoding" for very large numbers (>8 billion).
23 * Encoding is binary, with highest bit always set as a marker
24 * and sign in next-highest bit:
25 * 80 00 .. 00 - zero
26 * bf ff .. ff - largest positive number
27 * ff ff .. ff - minus 1
28 * c0 00 .. 00 - smallest negative number
29 *
30 * We expect it only in size field, where negative numbers don't make sense.
31 */
32static off_t getBase256_len12(const char *str)
33{
34 off_t value;
35 int len;
36
37 /* if (*str & 0x40) error; - caller prevents this */
38
39 if (sizeof(off_t) >= 12) {
40 /* Probably 128-bit (16 byte) off_t. Can be optimized. */
41 len = 12;
42 value = *str++ & 0x3f;
43 while (--len)
44 value = (value << 8) + (unsigned char) *str++;
45 return value;
46 }
47
48#ifdef CHECK_FOR_OVERFLOW
49 /* Can be optimized to eat 32-bit chunks */
50 char c = *str++ & 0x3f;
51 len = 12;
52 while (1) {
53 if (c)
54 bb_error_msg_and_die("overflow in base-256 encoded file size");
55 if (--len == sizeof(off_t))
56 break;
57 c = *str++;
58 }
59#else
60 str += (12 - sizeof(off_t));
61#endif
62
63/* Now str points to sizeof(off_t) least significant bytes.
64 *
65 * Example of tar file with 8914993153 (0x213600001) byte file.
66 * Field starts at offset 7c:
67 * 00070 30 30 30 00 30 30 30 30 30 30 30 00 80 00 00 00 |000.0000000.....|
68 * 00080 00 00 00 02 13 60 00 01 31 31 31 32 30 33 33 36 |.....`..11120336|
69 *
70 * str is at offset 80 or 84 now (64-bit or 32-bit off_t).
71 * We (ab)use the fact that value happens to be aligned,
72 * and fetch it in one go:
73 */
74 if (sizeof(off_t) == 8) {
75 value = *(aliased_off_t*)str;
76 value = SWAP_BE64(value);
77 } else if (sizeof(off_t) == 4) {
78 value = *(aliased_off_t*)str;
79 value = SWAP_BE32(value);
80 } else {
81 value = 0;
82 len = sizeof(off_t);
83 while (--len)
84 value = (value << 8) + (unsigned char) *str++;
85 }
86 return value;
87}
88
89/* NB: _DESTROYS_ str[len] character! */ 21/* NB: _DESTROYS_ str[len] character! */
90static unsigned long long getOctal(char *str, int len) 22static unsigned long long getOctal(char *str, int len)
91{ 23{
92 unsigned long long v; 24 unsigned long long v;
25 char *end;
93 /* NB: leading spaces are allowed. Using strtoull to handle that. 26 /* NB: leading spaces are allowed. Using strtoull to handle that.
94 * The downside is that we accept e.g. "-123" too :( 27 * The downside is that we accept e.g. "-123" too :(
95 */ 28 */
96 str[len] = '\0'; 29 str[len] = '\0';
97 v = strtoull(str, &str, 8); 30 v = strtoull(str, &end, 8);
98 /* std: "Each numeric field is terminated by one or more 31 /* std: "Each numeric field is terminated by one or more
99 * <space> or NUL characters". We must support ' '! */ 32 * <space> or NUL characters". We must support ' '! */
100 if (*str != '\0' && *str != ' ') 33 if (*end != '\0' && *end != ' ') {
101 bb_error_msg_and_die("corrupted octal value in tar header"); 34 int8_t first = str[0];
35 if (!(first & 0x80))
36 bb_error_msg_and_die("corrupted octal value in tar header");
37 /*
38 * GNU tar uses "base-256 encoding" for very large numbers.
39 * Encoding is binary, with highest bit always set as a marker
40 * and sign in next-highest bit:
41 * 80 00 .. 00 - zero
42 * bf ff .. ff - largest positive number
43 * ff ff .. ff - minus 1
44 * c0 00 .. 00 - smallest negative number
45 *
46 * Example of tar file with 8914993153 (0x213600001) byte file.
47 * Field starts at offset 7c:
48 * 00070 30 30 30 00 30 30 30 30 30 30 30 00 80 00 00 00 |000.0000000.....|
49 * 00080 00 00 00 02 13 60 00 01 31 31 31 32 30 33 33 36 |.....`..11120336|
50 *
51 * NB: tarballs with NEGATIVE unix times encoded that way were seen!
52 */
53 v = first;
54 /* Sign-extend using 6th bit: */
55 v <<= sizeof(unsigned long long)*8 - 7;
56 v = (long long)v >> (sizeof(unsigned long long)*8 - 7);
57 while (--len != 0)
58 v = (v << 8) + (unsigned char) *str++;
59 }
102 return v; 60 return v;
103} 61}
104#define GET_OCTAL(a) getOctal((a), sizeof(a)) 62#define GET_OCTAL(a) getOctal((a), sizeof(a))
@@ -358,15 +316,8 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
358 file_header->tar__uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL; 316 file_header->tar__uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL;
359 file_header->tar__gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL; 317 file_header->tar__gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL;
360#endif 318#endif
361 /* mtime: rudimentally handle GNU tar's "base256 encoding" 319 file_header->mtime = GET_OCTAL(tar.mtime);
362 * People report tarballs with NEGATIVE unix times encoded that way */ 320 file_header->size = GET_OCTAL(tar.size);
363 file_header->mtime = (tar.mtime[0] & 0x80) /* base256? */
364 ? 0 /* bogus */
365 : GET_OCTAL(tar.mtime);
366 /* size: handle GNU tar's "base256 encoding" */
367 file_header->size = (tar.size[0] & 0xc0) == 0x80 /* positive base256? */
368 ? getBase256_len12(tar.size)
369 : GET_OCTAL(tar.size);
370 file_header->gid = GET_OCTAL(tar.gid); 321 file_header->gid = GET_OCTAL(tar.gid);
371 file_header->uid = GET_OCTAL(tar.uid); 322 file_header->uid = GET_OCTAL(tar.uid);
372 /* Set bits 0-11 of the files mode */ 323 /* Set bits 0-11 of the files mode */