14 |
#include "libbb.h" |
#include "libbb.h" |
15 |
#include "unarchive.h" |
#include "unarchive.h" |
16 |
|
|
|
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
|
|
static char *longname; |
|
|
static char *linkname; |
|
|
#else |
|
|
enum { |
|
|
longname = 0, |
|
|
linkname = 0, |
|
|
}; |
|
|
#endif |
|
|
|
|
17 |
/* NB: _DESTROYS_ str[len] character! */ |
/* NB: _DESTROYS_ str[len] character! */ |
18 |
static unsigned long long getOctal(char *str, int len) |
static unsigned long long getOctal(char *str, int len) |
19 |
{ |
{ |
20 |
unsigned long long v; |
unsigned long long v; |
21 |
/* Actually, tar header allows leading spaces also. |
/* NB: leading spaces are allowed. Using strtoull to handle that. |
22 |
* Oh well, we will be liberal and skip this... |
* The downside is that we accept e.g. "-123" too :) |
23 |
* The only downside probably is that we allow "-123" too :) |
*/ |
|
if (*str < '0' || *str > '7') |
|
|
bb_error_msg_and_die("corrupted octal value in tar header"); |
|
|
*/ |
|
24 |
str[len] = '\0'; |
str[len] = '\0'; |
25 |
v = strtoull(str, &str, 8); |
v = strtoull(str, &str, 8); |
26 |
if (*str) |
if (*str && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY || *str != ' ')) |
27 |
bb_error_msg_and_die("corrupted octal value in tar header"); |
bb_error_msg_and_die("corrupted octal value in tar header"); |
28 |
return v; |
return v; |
29 |
} |
} |
30 |
#define GET_OCTAL(a) getOctal((a), sizeof(a)) |
#define GET_OCTAL(a) getOctal((a), sizeof(a)) |
31 |
|
|
32 |
void BUG_tar_header_size(void); |
void BUG_tar_header_size(void); |
33 |
char get_header_tar(archive_handle_t *archive_handle) |
char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) |
34 |
{ |
{ |
|
static int end; |
|
|
|
|
35 |
file_header_t *file_header = archive_handle->file_header; |
file_header_t *file_header = archive_handle->file_header; |
36 |
struct { |
struct { |
37 |
/* ustar header, Posix 1003.1 */ |
/* ustar header, Posix 1003.1 */ |
44 |
char chksum[8]; /* 148-155 */ |
char chksum[8]; /* 148-155 */ |
45 |
char typeflag; /* 156-156 */ |
char typeflag; /* 156-156 */ |
46 |
char linkname[100]; /* 157-256 */ |
char linkname[100]; /* 157-256 */ |
47 |
char magic[6]; /* 257-262 */ |
/* POSIX: "ustar" NUL "00" */ |
48 |
char version[2]; /* 263-264 */ |
/* GNU tar: "ustar " NUL */ |
49 |
|
/* Normally it's defined as magic[6] followed by |
50 |
|
* version[2], but we put them together to simplify code |
51 |
|
*/ |
52 |
|
char magic[8]; /* 257-264 */ |
53 |
char uname[32]; /* 265-296 */ |
char uname[32]; /* 265-296 */ |
54 |
char gname[32]; /* 297-328 */ |
char gname[32]; /* 297-328 */ |
55 |
char devmajor[8]; /* 329-336 */ |
char devmajor[8]; /* 329-336 */ |
58 |
char padding[12]; /* 500-512 */ |
char padding[12]; /* 500-512 */ |
59 |
} tar; |
} tar; |
60 |
char *cp; |
char *cp; |
61 |
int sum, i; |
int i, sum_u, sum; |
62 |
|
#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY |
63 |
|
int sum_s; |
64 |
|
#endif |
65 |
int parse_names; |
int parse_names; |
66 |
|
|
67 |
|
/* Our "private data" */ |
68 |
|
#define p_end (*(smallint *)(&archive_handle->ah_priv[0])) |
69 |
|
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
70 |
|
#define p_longname (*(char* *)(&archive_handle->ah_priv[1])) |
71 |
|
#define p_linkname (*(char* *)(&archive_handle->ah_priv[2])) |
72 |
|
#else |
73 |
|
#define p_longname 0 |
74 |
|
#define p_linkname 0 |
75 |
|
#endif |
76 |
|
// if (!archive_handle->ah_priv_inited) { |
77 |
|
// archive_handle->ah_priv_inited = 1; |
78 |
|
// p_end = 0; |
79 |
|
// USE_FEATURE_TAR_GNU_EXTENSIONS(p_longname = NULL;) |
80 |
|
// USE_FEATURE_TAR_GNU_EXTENSIONS(p_linkname = NULL;) |
81 |
|
// } |
82 |
|
|
83 |
if (sizeof(tar) != 512) |
if (sizeof(tar) != 512) |
84 |
BUG_tar_header_size(); |
BUG_tar_header_size(); |
85 |
|
|
91 |
|
|
92 |
again_after_align: |
again_after_align: |
93 |
|
|
94 |
xread(archive_handle->src_fd, &tar, 512); |
#if ENABLE_DESKTOP || ENABLE_FEATURE_TAR_AUTODETECT |
95 |
archive_handle->offset += 512; |
/* to prevent misdetection of bz2 sig */ |
96 |
|
*(uint32_t*)(&tar) = 0; |
97 |
|
i = full_read(archive_handle->src_fd, &tar, 512); |
98 |
|
/* If GNU tar sees EOF in above read, it says: |
99 |
|
* "tar: A lone zero block at N", where N = kilobyte |
100 |
|
* where EOF was met (not EOF block, actual EOF!), |
101 |
|
* and exits with EXIT_SUCCESS. |
102 |
|
* We will mimic exit(EXIT_SUCCESS), although we will not mimic |
103 |
|
* the message and we don't check whether we indeed |
104 |
|
* saw zero block directly before this. */ |
105 |
|
if (i == 0) { |
106 |
|
xfunc_error_retval = 0; |
107 |
|
short_read: |
108 |
|
bb_error_msg_and_die("short read"); |
109 |
|
} |
110 |
|
if (i != 512) { |
111 |
|
USE_FEATURE_TAR_AUTODETECT(goto autodetect;) |
112 |
|
goto short_read; |
113 |
|
} |
114 |
|
|
115 |
|
#else |
116 |
|
i = 512; |
117 |
|
xread(archive_handle->src_fd, &tar, i); |
118 |
|
#endif |
119 |
|
archive_handle->offset += i; |
120 |
|
|
121 |
/* If there is no filename its an empty header */ |
/* If there is no filename its an empty header */ |
122 |
if (tar.name[0] == 0) { |
if (tar.name[0] == 0 && tar.prefix[0] == 0) { |
123 |
if (end) { |
if (p_end) { |
124 |
/* This is the second consecutive empty header! End of archive! |
/* Second consecutive empty header - end of archive. |
125 |
* Read until the end to empty the pipe from gz or bz2 |
* Read until the end to empty the pipe from gz or bz2 |
126 |
*/ |
*/ |
127 |
while (full_read(archive_handle->src_fd, &tar, 512) == 512) |
while (full_read(archive_handle->src_fd, &tar, 512) == 512) |
128 |
/* repeat */; |
continue; |
129 |
return EXIT_FAILURE; |
return EXIT_FAILURE; |
130 |
} |
} |
131 |
end = 1; |
p_end = 1; |
132 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
133 |
} |
} |
134 |
end = 0; |
p_end = 0; |
135 |
|
|
136 |
/* Check header has valid magic, "ustar" is for the proper tar |
/* Check header has valid magic, "ustar" is for the proper tar, |
137 |
* 0's are for the old tar format |
* five NULs are for the old tar format */ |
138 |
*/ |
if (strncmp(tar.magic, "ustar", 5) != 0 |
139 |
if (strncmp(tar.magic, "ustar", 5) != 0) { |
&& (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY |
140 |
#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY |
|| memcmp(tar.magic, "\0\0\0\0", 5) != 0) |
141 |
if (memcmp(tar.magic, "\0\0\0\0", 5) != 0) |
) { |
142 |
|
#if ENABLE_FEATURE_TAR_AUTODETECT |
143 |
|
char FAST_FUNC (*get_header_ptr)(archive_handle_t *); |
144 |
|
|
145 |
|
autodetect: |
146 |
|
/* tar gz/bz autodetect: check for gz/bz2 magic. |
147 |
|
* If we see the magic, and it is the very first block, |
148 |
|
* we can switch to get_header_tar_gz/bz2/lzma(). |
149 |
|
* Needs seekable fd. I wish recv(MSG_PEEK) works |
150 |
|
* on any fd... */ |
151 |
|
#if ENABLE_FEATURE_SEAMLESS_GZ |
152 |
|
if (tar.name[0] == 0x1f && tar.name[1] == (char)0x8b) { /* gzip */ |
153 |
|
get_header_ptr = get_header_tar_gz; |
154 |
|
} else |
155 |
|
#endif |
156 |
|
#if ENABLE_FEATURE_SEAMLESS_BZ2 |
157 |
|
if (tar.name[0] == 'B' && tar.name[1] == 'Z' |
158 |
|
&& tar.name[2] == 'h' && isdigit(tar.name[3]) |
159 |
|
) { /* bzip2 */ |
160 |
|
get_header_ptr = get_header_tar_bz2; |
161 |
|
} else |
162 |
|
#endif |
163 |
|
goto err; |
164 |
|
/* Two different causes for lseek() != 0: |
165 |
|
* unseekable fd (would like to support that too, but...), |
166 |
|
* or not first block (false positive, it's not .gz/.bz2!) */ |
167 |
|
if (lseek(archive_handle->src_fd, -i, SEEK_CUR) != 0) |
168 |
|
goto err; |
169 |
|
while (get_header_ptr(archive_handle) == EXIT_SUCCESS) |
170 |
|
continue; |
171 |
|
return EXIT_FAILURE; |
172 |
|
err: |
173 |
|
#endif /* FEATURE_TAR_AUTODETECT */ |
174 |
|
bb_error_msg_and_die("invalid tar magic"); |
175 |
|
} |
176 |
|
|
177 |
|
/* Do checksum on headers. |
178 |
|
* POSIX says that checksum is done on unsigned bytes, but |
179 |
|
* Sun and HP-UX gets it wrong... more details in |
180 |
|
* GNU tar source. */ |
181 |
|
#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY |
182 |
|
sum_s = ' ' * sizeof(tar.chksum); |
183 |
|
#endif |
184 |
|
sum_u = ' ' * sizeof(tar.chksum); |
185 |
|
for (i = 0; i < 148; i++) { |
186 |
|
sum_u += ((unsigned char*)&tar)[i]; |
187 |
|
#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY |
188 |
|
sum_s += ((signed char*)&tar)[i]; |
189 |
#endif |
#endif |
|
bb_error_msg_and_die("invalid tar magic"); |
|
190 |
} |
} |
191 |
/* Do checksum on headers */ |
for (i = 156; i < 512; i++) { |
192 |
sum = ' ' * sizeof(tar.chksum); |
sum_u += ((unsigned char*)&tar)[i]; |
193 |
for (i = 0; i < 148 ; i++) { |
#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY |
194 |
sum += ((char*)&tar)[i]; |
sum_s += ((signed char*)&tar)[i]; |
195 |
|
#endif |
196 |
} |
} |
197 |
for (i = 156; i < 512 ; i++) { |
#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY |
198 |
sum += ((char*)&tar)[i]; |
sum = strtoul(tar.chksum, &cp, 8); |
199 |
|
if ((*cp && *cp != ' ') |
200 |
|
|| (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) |
201 |
|
) { |
202 |
|
bb_error_msg_and_die("invalid tar header checksum"); |
203 |
} |
} |
204 |
|
#else |
205 |
/* This field does not need special treatment (getOctal) */ |
/* This field does not need special treatment (getOctal) */ |
206 |
if (sum != xstrtoul(tar.chksum, 8)) { |
sum = xstrtoul(tar.chksum, 8); |
207 |
|
if (sum_u != sum USE_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) { |
208 |
bb_error_msg_and_die("invalid tar header checksum"); |
bb_error_msg_and_die("invalid tar header checksum"); |
209 |
} |
} |
210 |
|
#endif |
211 |
|
|
212 |
/* 0 is reserved for high perf file, treat as normal file */ |
/* 0 is reserved for high perf file, treat as normal file */ |
213 |
if (!tar.typeflag) tar.typeflag = '0'; |
if (!tar.typeflag) tar.typeflag = '0'; |
216 |
/* getOctal trashes subsequent field, therefore we call it |
/* getOctal trashes subsequent field, therefore we call it |
217 |
* on fields in reverse order */ |
* on fields in reverse order */ |
218 |
if (tar.devmajor[0]) { |
if (tar.devmajor[0]) { |
219 |
|
char t = tar.prefix[0]; |
220 |
|
/* we trash prefix[0] here, but we DO need it later! */ |
221 |
unsigned minor = GET_OCTAL(tar.devminor); |
unsigned minor = GET_OCTAL(tar.devminor); |
222 |
unsigned major = GET_OCTAL(tar.devmajor); |
unsigned major = GET_OCTAL(tar.devmajor); |
223 |
file_header->device = makedev(major, minor); |
file_header->device = makedev(major, minor); |
224 |
|
tar.prefix[0] = t; |
225 |
} |
} |
226 |
file_header->link_name = NULL; |
file_header->link_target = NULL; |
227 |
if (!linkname && parse_names && tar.linkname[0]) { |
if (!p_linkname && parse_names && tar.linkname[0]) { |
228 |
/* we trash magic[0] here, it's ok */ |
file_header->link_target = xstrndup(tar.linkname, sizeof(tar.linkname)); |
229 |
tar.linkname[sizeof(tar.linkname)] = '\0'; |
/* FIXME: what if we have non-link object with link_target? */ |
230 |
file_header->link_name = xstrdup(tar.linkname); |
/* Will link_target be free()ed? */ |
231 |
/* FIXME: what if we have non-link object with link_name? */ |
} |
232 |
/* Will link_name be free()ed? */ |
#if ENABLE_FEATURE_TAR_UNAME_GNAME |
233 |
} |
file_header->uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL; |
234 |
|
file_header->gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL; |
235 |
|
#endif |
236 |
file_header->mtime = GET_OCTAL(tar.mtime); |
file_header->mtime = GET_OCTAL(tar.mtime); |
237 |
file_header->size = GET_OCTAL(tar.size); |
file_header->size = GET_OCTAL(tar.size); |
238 |
file_header->gid = GET_OCTAL(tar.gid); |
file_header->gid = GET_OCTAL(tar.gid); |
241 |
file_header->mode = 07777 & GET_OCTAL(tar.mode); |
file_header->mode = 07777 & GET_OCTAL(tar.mode); |
242 |
|
|
243 |
file_header->name = NULL; |
file_header->name = NULL; |
244 |
if (!longname && parse_names) { |
if (!p_longname && parse_names) { |
245 |
/* we trash mode[0] here, it's ok */ |
/* we trash mode[0] here, it's ok */ |
246 |
tar.name[sizeof(tar.name)] = '\0'; |
//tar.name[sizeof(tar.name)] = '\0'; - gcc 4.3.0 would complain |
247 |
|
tar.mode[0] = '\0'; |
248 |
if (tar.prefix[0]) { |
if (tar.prefix[0]) { |
249 |
/* and padding[0] */ |
/* and padding[0] */ |
250 |
tar.prefix[sizeof(tar.prefix)] = '\0'; |
//tar.prefix[sizeof(tar.prefix)] = '\0'; - gcc 4.3.0 would complain |
251 |
|
tar.padding[0] = '\0'; |
252 |
file_header->name = concat_path_file(tar.prefix, tar.name); |
file_header->name = concat_path_file(tar.prefix, tar.name); |
253 |
} else |
} else |
254 |
file_header->name = xstrdup(tar.name); |
file_header->name = xstrdup(tar.name); |
266 |
case '0': |
case '0': |
267 |
#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY |
#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY |
268 |
if (last_char_is(file_header->name, '/')) { |
if (last_char_is(file_header->name, '/')) { |
269 |
file_header->mode |= S_IFDIR; |
goto set_dir; |
270 |
} else |
} |
271 |
#endif |
#endif |
272 |
file_header->mode |= S_IFREG; |
file_header->mode |= S_IFREG; |
273 |
break; |
break; |
274 |
case '2': |
case '2': |
275 |
file_header->mode |= S_IFLNK; |
file_header->mode |= S_IFLNK; |
276 |
|
/* have seen tarballs with size field containing |
277 |
|
* the size of the link target's name */ |
278 |
|
size0: |
279 |
|
file_header->size = 0; |
280 |
break; |
break; |
281 |
case '3': |
case '3': |
282 |
file_header->mode |= S_IFCHR; |
file_header->mode |= S_IFCHR; |
283 |
break; |
goto size0; /* paranoia */ |
284 |
case '4': |
case '4': |
285 |
file_header->mode |= S_IFBLK; |
file_header->mode |= S_IFBLK; |
286 |
break; |
goto size0; |
287 |
case '5': |
case '5': |
288 |
|
USE_FEATURE_TAR_OLDGNU_COMPATIBILITY(set_dir:) |
289 |
file_header->mode |= S_IFDIR; |
file_header->mode |= S_IFDIR; |
290 |
break; |
goto size0; |
291 |
case '6': |
case '6': |
292 |
file_header->mode |= S_IFIFO; |
file_header->mode |= S_IFIFO; |
293 |
break; |
goto size0; |
294 |
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
295 |
case 'L': |
case 'L': |
296 |
/* free: paranoia: tar with several consecutive longnames */ |
/* free: paranoia: tar with several consecutive longnames */ |
297 |
free(longname); |
free(p_longname); |
298 |
/* For paranoia reasons we allocate extra NUL char */ |
/* For paranoia reasons we allocate extra NUL char */ |
299 |
longname = xzalloc(file_header->size + 1); |
p_longname = xzalloc(file_header->size + 1); |
300 |
/* We read ASCIZ string, including NUL */ |
/* We read ASCIZ string, including NUL */ |
301 |
xread(archive_handle->src_fd, longname, file_header->size); |
xread(archive_handle->src_fd, p_longname, file_header->size); |
302 |
archive_handle->offset += file_header->size; |
archive_handle->offset += file_header->size; |
303 |
/* return get_header_tar(archive_handle); */ |
/* return get_header_tar(archive_handle); */ |
304 |
/* gcc 4.1.1 didn't optimize it into jump */ |
/* gcc 4.1.1 didn't optimize it into jump */ |
305 |
/* so we will do it ourself, this also saves stack */ |
/* so we will do it ourself, this also saves stack */ |
306 |
goto again; |
goto again; |
307 |
case 'K': |
case 'K': |
308 |
free(linkname); |
free(p_linkname); |
309 |
linkname = xzalloc(file_header->size + 1); |
p_linkname = xzalloc(file_header->size + 1); |
310 |
xread(archive_handle->src_fd, linkname, file_header->size); |
xread(archive_handle->src_fd, p_linkname, file_header->size); |
311 |
archive_handle->offset += file_header->size; |
archive_handle->offset += file_header->size; |
312 |
/* return get_header_tar(archive_handle); */ |
/* return get_header_tar(archive_handle); */ |
313 |
goto again; |
goto again; |
334 |
} |
} |
335 |
|
|
336 |
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
337 |
if (longname) { |
if (p_longname) { |
338 |
file_header->name = longname; |
file_header->name = p_longname; |
339 |
longname = NULL; |
p_longname = NULL; |
340 |
} |
} |
341 |
if (linkname) { |
if (p_linkname) { |
342 |
file_header->link_name = linkname; |
file_header->link_target = p_linkname; |
343 |
linkname = NULL; |
p_linkname = NULL; |
344 |
} |
} |
345 |
#endif |
#endif |
346 |
if (!strncmp(file_header->name, "/../"+1, 3) |
if (strncmp(file_header->name, "/../"+1, 3) == 0 |
347 |
|| strstr(file_header->name, "/../") |
|| strstr(file_header->name, "/../") |
348 |
) { |
) { |
349 |
bb_error_msg_and_die("name with '..' encountered: '%s'", |
bb_error_msg_and_die("name with '..' encountered: '%s'", |
355 |
cp = last_char_is(file_header->name, '/'); |
cp = last_char_is(file_header->name, '/'); |
356 |
|
|
357 |
if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { |
if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { |
358 |
archive_handle->action_header(archive_handle->file_header); |
archive_handle->action_header(/*archive_handle->*/ file_header); |
359 |
/* Note that we kill the '/' only after action_header() */ |
/* Note that we kill the '/' only after action_header() */ |
360 |
/* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ |
/* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ |
361 |
if (cp) *cp = '\0'; |
if (cp) *cp = '\0'; |
362 |
archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; |
archive_handle->ah_flags |= ARCHIVE_EXTRACT_QUIET; |
363 |
archive_handle->action_data(archive_handle); |
archive_handle->action_data(archive_handle); |
364 |
llist_add_to(&(archive_handle->passed), file_header->name); |
llist_add_to(&(archive_handle->passed), file_header->name); |
365 |
} else { |
} else { |
368 |
} |
} |
369 |
archive_handle->offset += file_header->size; |
archive_handle->offset += file_header->size; |
370 |
|
|
371 |
free(file_header->link_name); |
free(file_header->link_target); |
372 |
/* Do not free(file_header->name)! */ |
/* Do not free(file_header->name)! (why?) */ |
373 |
|
#if ENABLE_FEATURE_TAR_UNAME_GNAME |
374 |
|
free(file_header->uname); |
375 |
|
free(file_header->gname); |
376 |
|
#endif |
377 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
378 |
} |
} |