14 |
#include "libbb.h" |
#include "libbb.h" |
15 |
#include "unarchive.h" |
#include "unarchive.h" |
16 |
|
|
17 |
|
/* |
18 |
|
* GNU tar uses "base-256 encoding" for very large numbers (>8 billion). |
19 |
|
* Encoding is binary, with highest bit always set as a marker |
20 |
|
* and sign in next-highest bit: |
21 |
|
* 80 00 .. 00 - zero |
22 |
|
* bf ff .. ff - largest positive number |
23 |
|
* ff ff .. ff - minus 1 |
24 |
|
* c0 00 .. 00 - smallest negative number |
25 |
|
* |
26 |
|
* We expect it only in size field, where negative numbers don't make sense. |
27 |
|
*/ |
28 |
|
static off_t getBase256_len12(const char *str) |
29 |
|
{ |
30 |
|
off_t value; |
31 |
|
int len; |
32 |
|
|
33 |
|
/* if (*str & 0x40) error; - caller prevents this */ |
34 |
|
|
35 |
|
if (sizeof(off_t) >= 12) { |
36 |
|
/* Probably 128-bit (16 byte) off_t. Can be optimized. */ |
37 |
|
len = 12; |
38 |
|
value = *str++ & 0x3f; |
39 |
|
while (--len) |
40 |
|
value = (value << 8) + (unsigned char) *str++; |
41 |
|
return value; |
42 |
|
} |
43 |
|
|
44 |
|
#ifdef CHECK_FOR_OVERFLOW |
45 |
|
/* Can be optimized to eat 32-bit chunks */ |
46 |
|
char c = *str++ & 0x3f; |
47 |
|
len = 12; |
48 |
|
while (1) { |
49 |
|
if (c) |
50 |
|
bb_error_msg_and_die("overflow in base-256 encoded file size"); |
51 |
|
if (--len == sizeof(off_t)) |
52 |
|
break; |
53 |
|
c = *str++; |
54 |
|
} |
55 |
|
#else |
56 |
|
str += (12 - sizeof(off_t)); |
57 |
|
#endif |
58 |
|
|
59 |
|
/* Now str points to sizeof(off_t) least significant bytes. |
60 |
|
* |
61 |
|
* Example of tar file with 8914993153 (0x213600001) byte file. |
62 |
|
* Field starts at offset 7c: |
63 |
|
* 00070 30 30 30 00 30 30 30 30 30 30 30 00 80 00 00 00 |000.0000000.....| |
64 |
|
* 00080 00 00 00 02 13 60 00 01 31 31 31 32 30 33 33 36 |.....`..11120336| |
65 |
|
* |
66 |
|
* str is at offset 80 or 84 now (64-bit or 32-bit off_t). |
67 |
|
* We (ab)use the fact that value happens to be aligned, |
68 |
|
* and fetch it in one go: |
69 |
|
*/ |
70 |
|
if (sizeof(off_t) == 8) { |
71 |
|
value = *(off_t*)str; |
72 |
|
value = SWAP_BE64(value); |
73 |
|
} else if (sizeof(off_t) == 4) { |
74 |
|
value = *(off_t*)str; |
75 |
|
value = SWAP_BE32(value); |
76 |
|
} else { |
77 |
|
value = 0; |
78 |
|
len = sizeof(off_t); |
79 |
|
while (--len) |
80 |
|
value = (value << 8) + (unsigned char) *str++; |
81 |
|
} |
82 |
|
return value; |
83 |
|
} |
84 |
|
|
85 |
/* NB: _DESTROYS_ str[len] character! */ |
/* NB: _DESTROYS_ str[len] character! */ |
86 |
static unsigned long long getOctal(char *str, int len) |
static unsigned long long getOctal(char *str, int len) |
87 |
{ |
{ |
88 |
unsigned long long v; |
unsigned long long v; |
89 |
/* NB: leading spaces are allowed. Using strtoull to handle that. |
/* NB: leading spaces are allowed. Using strtoull to handle that. |
90 |
* The downside is that we accept e.g. "-123" too :) |
* The downside is that we accept e.g. "-123" too :( |
91 |
*/ |
*/ |
92 |
str[len] = '\0'; |
str[len] = '\0'; |
93 |
v = strtoull(str, &str, 8); |
v = strtoull(str, &str, 8); |
94 |
if (*str && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || *str != ' ')) |
/* std: "Each numeric field is terminated by one or more |
95 |
|
* <space> or NUL characters". We must support ' '! */ |
96 |
|
if (*str != '\0' && *str != ' ') |
97 |
bb_error_msg_and_die("corrupted octal value in tar header"); |
bb_error_msg_and_die("corrupted octal value in tar header"); |
98 |
return v; |
return v; |
99 |
} |
} |
135 |
int parse_names; |
int parse_names; |
136 |
|
|
137 |
/* Our "private data" */ |
/* Our "private data" */ |
|
#define p_end (*(smallint *)(&archive_handle->ah_priv[0])) |
|
138 |
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
139 |
#define p_longname (*(char* *)(&archive_handle->ah_priv[1])) |
# define p_longname (archive_handle->tar__longname) |
140 |
#define p_linkname (*(char* *)(&archive_handle->ah_priv[2])) |
# define p_linkname (archive_handle->tar__linkname) |
141 |
#else |
#else |
142 |
#define p_longname 0 |
# define p_longname 0 |
143 |
#define p_linkname 0 |
# define p_linkname 0 |
144 |
#endif |
#endif |
|
// if (!archive_handle->ah_priv_inited) { |
|
|
// archive_handle->ah_priv_inited = 1; |
|
|
// p_end = 0; |
|
|
// USE_FEATURE_TAR_GNU_EXTENSIONS(p_longname = NULL;) |
|
|
// USE_FEATURE_TAR_GNU_EXTENSIONS(p_linkname = NULL;) |
|
|
// } |
|
145 |
|
|
146 |
if (sizeof(tar) != 512) |
if (sizeof(tar) != 512) |
147 |
BUG_tar_header_size(); |
BUG_tar_header_size(); |
171 |
bb_error_msg_and_die("short read"); |
bb_error_msg_and_die("short read"); |
172 |
} |
} |
173 |
if (i != 512) { |
if (i != 512) { |
174 |
USE_FEATURE_TAR_AUTODETECT(goto autodetect;) |
IF_FEATURE_TAR_AUTODETECT(goto autodetect;) |
175 |
goto short_read; |
goto short_read; |
176 |
} |
} |
177 |
|
|
183 |
|
|
184 |
/* If there is no filename its an empty header */ |
/* If there is no filename its an empty header */ |
185 |
if (tar.name[0] == 0 && tar.prefix[0] == 0) { |
if (tar.name[0] == 0 && tar.prefix[0] == 0) { |
186 |
if (p_end) { |
if (archive_handle->tar__end) { |
187 |
/* Second consecutive empty header - end of archive. |
/* Second consecutive empty header - end of archive. |
188 |
* Read until the end to empty the pipe from gz or bz2 |
* Read until the end to empty the pipe from gz or bz2 |
189 |
*/ |
*/ |
191 |
continue; |
continue; |
192 |
return EXIT_FAILURE; |
return EXIT_FAILURE; |
193 |
} |
} |
194 |
p_end = 1; |
archive_handle->tar__end = 1; |
195 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
196 |
} |
} |
197 |
p_end = 0; |
archive_handle->tar__end = 0; |
198 |
|
|
199 |
/* Check header has valid magic, "ustar" is for the proper tar, |
/* Check header has valid magic, "ustar" is for the proper tar, |
200 |
* five NULs are for the old tar format */ |
* five NULs are for the old tar format */ |
257 |
sum_s += ((signed char*)&tar)[i]; |
sum_s += ((signed char*)&tar)[i]; |
258 |
#endif |
#endif |
259 |
} |
} |
|
#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 |
|
260 |
/* This field does not need special treatment (getOctal) */ |
/* This field does not need special treatment (getOctal) */ |
261 |
sum = xstrtoul(tar.chksum, 8); |
{ |
262 |
if (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) { |
char *endp; /* gcc likes temp var for &endp */ |
263 |
|
sum = strtoul(tar.chksum, &endp, 8); |
264 |
|
if ((*endp != '\0' && *endp != ' ') |
265 |
|
|| (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) |
266 |
|
) { |
267 |
|
bb_error_msg_and_die("invalid tar header checksum"); |
268 |
|
} |
269 |
|
} |
270 |
|
/* don't use xstrtoul, tar.chksum may have leading spaces */ |
271 |
|
sum = strtoul(tar.chksum, NULL, 8); |
272 |
|
if (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) { |
273 |
bb_error_msg_and_die("invalid tar header checksum"); |
bb_error_msg_and_die("invalid tar header checksum"); |
274 |
} |
} |
|
#endif |
|
275 |
|
|
276 |
/* 0 is reserved for high perf file, treat as normal file */ |
/* 0 is reserved for high perf file, treat as normal file */ |
277 |
if (!tar.typeflag) tar.typeflag = '0'; |
if (!tar.typeflag) tar.typeflag = '0'; |
294 |
/* Will link_target be free()ed? */ |
/* Will link_target be free()ed? */ |
295 |
} |
} |
296 |
#if ENABLE_FEATURE_TAR_UNAME_GNAME |
#if ENABLE_FEATURE_TAR_UNAME_GNAME |
297 |
file_header->uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL; |
file_header->tar__uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL; |
298 |
file_header->gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL; |
file_header->tar__gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL; |
299 |
#endif |
#endif |
300 |
file_header->mtime = GET_OCTAL(tar.mtime); |
/* mtime: rudimentally handle GNU tar's "base256 encoding" |
301 |
file_header->size = GET_OCTAL(tar.size); |
* People report tarballs with NEGATIVE unix times encoded that way */ |
302 |
|
file_header->mtime = (tar.mtime[0] & 0x80) /* base256? */ |
303 |
|
? 0 /* bogus */ |
304 |
|
: GET_OCTAL(tar.mtime); |
305 |
|
/* size: handle GNU tar's "base256 encoding" */ |
306 |
|
file_header->size = (tar.size[0] & 0xc0) == 0x80 /* positive base256? */ |
307 |
|
? getBase256_len12(tar.size) |
308 |
|
: GET_OCTAL(tar.size); |
309 |
file_header->gid = GET_OCTAL(tar.gid); |
file_header->gid = GET_OCTAL(tar.gid); |
310 |
file_header->uid = GET_OCTAL(tar.uid); |
file_header->uid = GET_OCTAL(tar.uid); |
311 |
/* Set bits 0-11 of the files mode */ |
/* Set bits 0-11 of the files mode */ |
356 |
file_header->mode |= S_IFBLK; |
file_header->mode |= S_IFBLK; |
357 |
goto size0; |
goto size0; |
358 |
case '5': |
case '5': |
359 |
USE_FEATURE_TAR_OLDGNU_COMPATIBILITY(set_dir:) |
IF_FEATURE_TAR_OLDGNU_COMPATIBILITY(set_dir:) |
360 |
file_header->mode |= S_IFDIR; |
file_header->mode |= S_IFDIR; |
361 |
goto size0; |
goto size0; |
362 |
case '6': |
case '6': |
442 |
free(file_header->link_target); |
free(file_header->link_target); |
443 |
/* Do not free(file_header->name)! (why?) */ |
/* Do not free(file_header->name)! (why?) */ |
444 |
#if ENABLE_FEATURE_TAR_UNAME_GNAME |
#if ENABLE_FEATURE_TAR_UNAME_GNAME |
445 |
free(file_header->uname); |
free(file_header->tar__uname); |
446 |
free(file_header->gname); |
free(file_header->tar__gname); |
447 |
#endif |
#endif |
448 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
449 |
} |
} |