diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-06 20:08:14 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-06 20:08:14 +0200 |
commit | 7b48eb4372c0b639bcb1eaedd63795c2dc0d9820 (patch) | |
tree | f22da6d2d3ce82aa6d2c51cac27ab793cac4750d | |
parent | c7efd6441def53df0200d7cce24e2956ad96aa02 (diff) | |
download | busybox-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.c | 111 |
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; | |||
18 | typedef off_t aliased_off_t FIX_ALIASING; | 18 | typedef 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 | */ | ||
32 | static 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! */ |
90 | static unsigned long long getOctal(char *str, int len) | 22 | static 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 */ |