diff options
author | Ron Yorston <rmy@pobox.com> | 2017-02-08 20:09:29 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-02-08 20:09:29 +0000 |
commit | 373275a708bafb88fa4f0519de2166154f44fed9 (patch) | |
tree | 4587b4fd3f695e0f3705b2a217e199f3144df931 /archival/unzip.c | |
parent | b74b2619779b1deb903b7766261807df1e9b1f7f (diff) | |
parent | c2b18583a3df06aeecf535c3cea6856aa1f2e205 (diff) | |
download | busybox-w32-373275a708bafb88fa4f0519de2166154f44fed9.tar.gz busybox-w32-373275a708bafb88fa4f0519de2166154f44fed9.tar.bz2 busybox-w32-373275a708bafb88fa4f0519de2166154f44fed9.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'archival/unzip.c')
-rw-r--r-- | archival/unzip.c | 489 |
1 files changed, 270 insertions, 219 deletions
diff --git a/archival/unzip.c b/archival/unzip.c index 9ad03d0e2..56989fa0b 100644 --- a/archival/unzip.c +++ b/archival/unzip.c | |||
@@ -34,6 +34,23 @@ | |||
34 | //config: ZIP files without deleted/updated files, SFX archives etc, | 34 | //config: ZIP files without deleted/updated files, SFX archives etc, |
35 | //config: you can reduce code size by unselecting this option. | 35 | //config: you can reduce code size by unselecting this option. |
36 | //config: To support less trivial ZIPs, say Y. | 36 | //config: To support less trivial ZIPs, say Y. |
37 | //config: | ||
38 | //config:config FEATURE_UNZIP_BZIP2 | ||
39 | //config: bool "Support compression method 12 (bzip2)" | ||
40 | //config: default y | ||
41 | //config: depends on FEATURE_UNZIP_CDF && DESKTOP | ||
42 | // FEATURE_UNZIP_CDF is needed, otherwise we can't find start of next file | ||
43 | // DESKTOP is needed to get back uncompressed length | ||
44 | //config: | ||
45 | //config:config FEATURE_UNZIP_LZMA | ||
46 | //config: bool "Support compression method 14 (lzma)" | ||
47 | //config: default y | ||
48 | //config: depends on FEATURE_UNZIP_CDF && DESKTOP | ||
49 | //config: | ||
50 | //config:config FEATURE_UNZIP_XZ | ||
51 | //config: bool "Support compression method 95 (xz)" | ||
52 | //config: default y | ||
53 | //config: depends on FEATURE_UNZIP_CDF && DESKTOP | ||
37 | 54 | ||
38 | //applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP)) | 55 | //applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP)) |
39 | //kbuild:lib-$(CONFIG_UNZIP) += unzip.o | 56 | //kbuild:lib-$(CONFIG_UNZIP) += unzip.o |
@@ -65,8 +82,8 @@ | |||
65 | enum { | 82 | enum { |
66 | #if BB_BIG_ENDIAN | 83 | #if BB_BIG_ENDIAN |
67 | ZIP_FILEHEADER_MAGIC = 0x504b0304, | 84 | ZIP_FILEHEADER_MAGIC = 0x504b0304, |
68 | ZIP_CDF_MAGIC = 0x504b0102, /* central directory's file header */ | 85 | ZIP_CDF_MAGIC = 0x504b0102, /* CDF item */ |
69 | ZIP_CDE_MAGIC = 0x504b0506, /* "end of central directory" record */ | 86 | ZIP_CDE_MAGIC = 0x504b0506, /* End of CDF */ |
70 | ZIP_DD_MAGIC = 0x504b0708, | 87 | ZIP_DD_MAGIC = 0x504b0708, |
71 | #else | 88 | #else |
72 | ZIP_FILEHEADER_MAGIC = 0x04034b50, | 89 | ZIP_FILEHEADER_MAGIC = 0x04034b50, |
@@ -94,16 +111,16 @@ typedef union { | |||
94 | /* filename follows (not NUL terminated) */ | 111 | /* filename follows (not NUL terminated) */ |
95 | /* extra field follows */ | 112 | /* extra field follows */ |
96 | /* data follows */ | 113 | /* data follows */ |
97 | } formatted PACKED; | 114 | } fmt PACKED; |
98 | } zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ | 115 | } zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ |
99 | 116 | ||
100 | #define FIX_ENDIANNESS_ZIP(zip_header) \ | 117 | #define FIX_ENDIANNESS_ZIP(zip) \ |
101 | do { if (BB_BIG_ENDIAN) { \ | 118 | do { if (BB_BIG_ENDIAN) { \ |
102 | (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ | 119 | (zip).fmt.crc32 = SWAP_LE32((zip).fmt.crc32 ); \ |
103 | (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ | 120 | (zip).fmt.cmpsize = SWAP_LE32((zip).fmt.cmpsize ); \ |
104 | (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ | 121 | (zip).fmt.ucmpsize = SWAP_LE32((zip).fmt.ucmpsize ); \ |
105 | (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ | 122 | (zip).fmt.filename_len = SWAP_LE16((zip).fmt.filename_len); \ |
106 | (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ | 123 | (zip).fmt.extra_len = SWAP_LE16((zip).fmt.extra_len ); \ |
107 | }} while (0) | 124 | }} while (0) |
108 | 125 | ||
109 | #define CDF_HEADER_LEN 42 | 126 | #define CDF_HEADER_LEN 42 |
@@ -121,39 +138,39 @@ typedef union { | |||
121 | uint32_t crc32; /* 12-15 */ | 138 | uint32_t crc32; /* 12-15 */ |
122 | uint32_t cmpsize; /* 16-19 */ | 139 | uint32_t cmpsize; /* 16-19 */ |
123 | uint32_t ucmpsize; /* 20-23 */ | 140 | uint32_t ucmpsize; /* 20-23 */ |
124 | uint16_t file_name_length; /* 24-25 */ | 141 | uint16_t filename_len; /* 24-25 */ |
125 | uint16_t extra_field_length; /* 26-27 */ | 142 | uint16_t extra_len; /* 26-27 */ |
126 | uint16_t file_comment_length; /* 28-29 */ | 143 | uint16_t file_comment_length; /* 28-29 */ |
127 | uint16_t disk_number_start; /* 30-31 */ | 144 | uint16_t disk_number_start; /* 30-31 */ |
128 | uint16_t internal_file_attributes; /* 32-33 */ | 145 | uint16_t internal_attributes; /* 32-33 */ |
129 | uint32_t external_file_attributes PACKED; /* 34-37 */ | 146 | uint32_t external_attributes PACKED; /* 34-37 */ |
130 | uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ | 147 | uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ |
131 | /* filename follows (not NUL terminated) */ | 148 | /* filename follows (not NUL terminated) */ |
132 | /* extra field follows */ | 149 | /* extra field follows */ |
133 | /* comment follows */ | 150 | /* file comment follows */ |
134 | } formatted PACKED; | 151 | } fmt PACKED; |
135 | } cdf_header_t; | 152 | } cdf_header_t; |
136 | 153 | ||
137 | #define FIX_ENDIANNESS_CDF(cdf_header) \ | 154 | #define FIX_ENDIANNESS_CDF(cdf) \ |
138 | do { if (BB_BIG_ENDIAN) { \ | 155 | do { if (BB_BIG_ENDIAN) { \ |
139 | (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ | 156 | (cdf).fmt.version_made_by = SWAP_LE16((cdf).fmt.version_made_by); \ |
140 | (cdf_header).formatted.version_needed = SWAP_LE16((cdf_header).formatted.version_needed); \ | 157 | (cdf).fmt.version_needed = SWAP_LE16((cdf).fmt.version_needed); \ |
141 | (cdf_header).formatted.method = SWAP_LE16((cdf_header).formatted.method ); \ | 158 | (cdf).fmt.method = SWAP_LE16((cdf).fmt.method ); \ |
142 | (cdf_header).formatted.modtime = SWAP_LE16((cdf_header).formatted.modtime ); \ | 159 | (cdf).fmt.modtime = SWAP_LE16((cdf).fmt.modtime ); \ |
143 | (cdf_header).formatted.moddate = SWAP_LE16((cdf_header).formatted.moddate ); \ | 160 | (cdf).fmt.moddate = SWAP_LE16((cdf).fmt.moddate ); \ |
144 | (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ | 161 | (cdf).fmt.crc32 = SWAP_LE32((cdf).fmt.crc32 ); \ |
145 | (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ | 162 | (cdf).fmt.cmpsize = SWAP_LE32((cdf).fmt.cmpsize ); \ |
146 | (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ | 163 | (cdf).fmt.ucmpsize = SWAP_LE32((cdf).fmt.ucmpsize ); \ |
147 | (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ | 164 | (cdf).fmt.filename_len = SWAP_LE16((cdf).fmt.filename_len); \ |
148 | (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ | 165 | (cdf).fmt.extra_len = SWAP_LE16((cdf).fmt.extra_len ); \ |
149 | (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \ | 166 | (cdf).fmt.file_comment_length = SWAP_LE16((cdf).fmt.file_comment_length); \ |
150 | (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \ | 167 | (cdf).fmt.external_attributes = SWAP_LE32((cdf).fmt.external_attributes); \ |
151 | }} while (0) | 168 | }} while (0) |
152 | 169 | ||
153 | #define CDE_HEADER_LEN 16 | 170 | #define CDE_LEN 16 |
154 | 171 | ||
155 | typedef union { | 172 | typedef union { |
156 | uint8_t raw[CDE_HEADER_LEN]; | 173 | uint8_t raw[CDE_LEN]; |
157 | struct { | 174 | struct { |
158 | /* uint32_t signature; 50 4b 05 06 */ | 175 | /* uint32_t signature; 50 4b 05 06 */ |
159 | uint16_t this_disk_no; | 176 | uint16_t this_disk_no; |
@@ -162,14 +179,14 @@ typedef union { | |||
162 | uint16_t cdf_entries_total; | 179 | uint16_t cdf_entries_total; |
163 | uint32_t cdf_size; | 180 | uint32_t cdf_size; |
164 | uint32_t cdf_offset; | 181 | uint32_t cdf_offset; |
165 | /* uint16_t file_comment_length; */ | 182 | /* uint16_t archive_comment_length; */ |
166 | /* .ZIP file comment (variable size) */ | 183 | /* archive comment follows */ |
167 | } formatted PACKED; | 184 | } fmt PACKED; |
168 | } cde_header_t; | 185 | } cde_t; |
169 | 186 | ||
170 | #define FIX_ENDIANNESS_CDE(cde_header) \ | 187 | #define FIX_ENDIANNESS_CDE(cde) \ |
171 | do { if (BB_BIG_ENDIAN) { \ | 188 | do { if (BB_BIG_ENDIAN) { \ |
172 | (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ | 189 | (cde).fmt.cdf_offset = SWAP_LE32((cde).fmt.cdf_offset); \ |
173 | }} while (0) | 190 | }} while (0) |
174 | 191 | ||
175 | struct BUG { | 192 | struct BUG { |
@@ -178,13 +195,13 @@ struct BUG { | |||
178 | * even though the elements are all in the right place. | 195 | * even though the elements are all in the right place. |
179 | */ | 196 | */ |
180 | char BUG_zip_header_must_be_26_bytes[ | 197 | char BUG_zip_header_must_be_26_bytes[ |
181 | offsetof(zip_header_t, formatted.extra_len) + 2 | 198 | offsetof(zip_header_t, fmt.extra_len) + 2 |
182 | == ZIP_HEADER_LEN ? 1 : -1]; | 199 | == ZIP_HEADER_LEN ? 1 : -1]; |
183 | char BUG_cdf_header_must_be_42_bytes[ | 200 | char BUG_cdf_header_must_be_42_bytes[ |
184 | offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 | 201 | offsetof(cdf_header_t, fmt.relative_offset_of_local_header) + 4 |
185 | == CDF_HEADER_LEN ? 1 : -1]; | 202 | == CDF_HEADER_LEN ? 1 : -1]; |
186 | char BUG_cde_header_must_be_16_bytes[ | 203 | char BUG_cde_must_be_16_bytes[ |
187 | sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; | 204 | sizeof(cde_t) == CDE_LEN ? 1 : -1]; |
188 | }; | 205 | }; |
189 | 206 | ||
190 | 207 | ||
@@ -210,7 +227,7 @@ enum { zip_fd = 3 }; | |||
210 | /* NB: does not preserve file position! */ | 227 | /* NB: does not preserve file position! */ |
211 | static uint32_t find_cdf_offset(void) | 228 | static uint32_t find_cdf_offset(void) |
212 | { | 229 | { |
213 | cde_header_t cde_header; | 230 | cde_t cde; |
214 | unsigned char *buf; | 231 | unsigned char *buf; |
215 | unsigned char *p; | 232 | unsigned char *p; |
216 | off_t end; | 233 | off_t end; |
@@ -231,7 +248,7 @@ static uint32_t find_cdf_offset(void) | |||
231 | 248 | ||
232 | found = BAD_CDF_OFFSET; | 249 | found = BAD_CDF_OFFSET; |
233 | p = buf; | 250 | p = buf; |
234 | while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { | 251 | while (p <= buf + PEEK_FROM_END - CDE_LEN - 4) { |
235 | if (*p != 'P') { | 252 | if (*p != 'P') { |
236 | p++; | 253 | p++; |
237 | continue; | 254 | continue; |
@@ -243,19 +260,19 @@ static uint32_t find_cdf_offset(void) | |||
243 | if (*++p != 6) | 260 | if (*++p != 6) |
244 | continue; | 261 | continue; |
245 | /* we found CDE! */ | 262 | /* we found CDE! */ |
246 | memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); | 263 | memcpy(cde.raw, p + 1, CDE_LEN); |
247 | FIX_ENDIANNESS_CDE(cde_header); | 264 | FIX_ENDIANNESS_CDE(cde); |
248 | /* | 265 | /* |
249 | * I've seen .ZIP files with seemingly valid CDEs | 266 | * I've seen .ZIP files with seemingly valid CDEs |
250 | * where cdf_offset points past EOF - ?? | 267 | * where cdf_offset points past EOF - ?? |
251 | * This check ignores such CDEs: | 268 | * This check ignores such CDEs: |
252 | */ | 269 | */ |
253 | if (cde_header.formatted.cdf_offset < end + (p - buf)) { | 270 | if (cde.fmt.cdf_offset < end + (p - buf)) { |
254 | found = cde_header.formatted.cdf_offset; | 271 | found = cde.fmt.cdf_offset; |
255 | dbg("Possible cdf_offset:0x%x at 0x%"OFF_FMT"x", | 272 | dbg("Possible cdf_offset:0x%x at 0x%"OFF_FMT"x", |
256 | (unsigned)found, end + (p-3 - buf)); | 273 | (unsigned)found, end + (p-3 - buf)); |
257 | dbg(" cdf_offset+cdf_size:0x%x", | 274 | dbg(" cdf_offset+cdf_size:0x%x", |
258 | (unsigned)(found + SWAP_LE32(cde_header.formatted.cdf_size))); | 275 | (unsigned)(found + SWAP_LE32(cde.fmt.cdf_size))); |
259 | /* | 276 | /* |
260 | * We do not "break" here because only the last CDE is valid. | 277 | * We do not "break" here because only the last CDE is valid. |
261 | * I've seen a .zip archive which contained a .zip file, | 278 | * I've seen a .zip archive which contained a .zip file, |
@@ -269,7 +286,7 @@ static uint32_t find_cdf_offset(void) | |||
269 | return found; | 286 | return found; |
270 | }; | 287 | }; |
271 | 288 | ||
272 | static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) | 289 | static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf) |
273 | { | 290 | { |
274 | uint32_t magic; | 291 | uint32_t magic; |
275 | 292 | ||
@@ -279,23 +296,25 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) | |||
279 | dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); | 296 | dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); |
280 | xlseek(zip_fd, cdf_offset, SEEK_SET); | 297 | xlseek(zip_fd, cdf_offset, SEEK_SET); |
281 | xread(zip_fd, &magic, 4); | 298 | xread(zip_fd, &magic, 4); |
282 | /* Central Directory End? */ | 299 | /* Central Directory End? Assume CDF has ended. |
300 | * (more correct method is to use cde.cdf_entries_total counter) | ||
301 | */ | ||
283 | if (magic == ZIP_CDE_MAGIC) { | 302 | if (magic == ZIP_CDE_MAGIC) { |
284 | dbg("got ZIP_CDE_MAGIC"); | 303 | dbg("got ZIP_CDE_MAGIC"); |
285 | return 0; /* EOF */ | 304 | return 0; /* EOF */ |
286 | } | 305 | } |
287 | xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); | 306 | xread(zip_fd, cdf->raw, CDF_HEADER_LEN); |
288 | 307 | ||
289 | FIX_ENDIANNESS_CDF(*cdf_ptr); | 308 | FIX_ENDIANNESS_CDF(*cdf); |
290 | dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u", | 309 | dbg(" filename_len:%u extra_len:%u file_comment_length:%u", |
291 | (unsigned)cdf_ptr->formatted.file_name_length, | 310 | (unsigned)cdf->fmt.filename_len, |
292 | (unsigned)cdf_ptr->formatted.extra_field_length, | 311 | (unsigned)cdf->fmt.extra_len, |
293 | (unsigned)cdf_ptr->formatted.file_comment_length | 312 | (unsigned)cdf->fmt.file_comment_length |
294 | ); | 313 | ); |
295 | cdf_offset += 4 + CDF_HEADER_LEN | 314 | cdf_offset += 4 + CDF_HEADER_LEN |
296 | + cdf_ptr->formatted.file_name_length | 315 | + cdf->fmt.filename_len |
297 | + cdf_ptr->formatted.extra_field_length | 316 | + cdf->fmt.extra_len |
298 | + cdf_ptr->formatted.file_comment_length; | 317 | + cdf->fmt.file_comment_length; |
299 | 318 | ||
300 | return cdf_offset; | 319 | return cdf_offset; |
301 | }; | 320 | }; |
@@ -318,34 +337,67 @@ static void unzip_create_leading_dirs(const char *fn) | |||
318 | free(name); | 337 | free(name); |
319 | } | 338 | } |
320 | 339 | ||
321 | static void unzip_extract(zip_header_t *zip_header, int dst_fd) | 340 | static void unzip_extract(zip_header_t *zip, int dst_fd) |
322 | { | 341 | { |
323 | if (zip_header->formatted.method == 0) { | 342 | transformer_state_t xstate; |
343 | |||
344 | if (zip->fmt.method == 0) { | ||
324 | /* Method 0 - stored (not compressed) */ | 345 | /* Method 0 - stored (not compressed) */ |
325 | off_t size = zip_header->formatted.ucmpsize; | 346 | off_t size = zip->fmt.ucmpsize; |
326 | if (size) | 347 | if (size) |
327 | bb_copyfd_exact_size(zip_fd, dst_fd, size); | 348 | bb_copyfd_exact_size(zip_fd, dst_fd, size); |
328 | } else { | 349 | return; |
350 | } | ||
351 | |||
352 | init_transformer_state(&xstate); | ||
353 | xstate.bytes_in = zip->fmt.cmpsize; | ||
354 | xstate.src_fd = zip_fd; | ||
355 | xstate.dst_fd = dst_fd; | ||
356 | if (zip->fmt.method == 8) { | ||
329 | /* Method 8 - inflate */ | 357 | /* Method 8 - inflate */ |
330 | transformer_state_t xstate; | ||
331 | init_transformer_state(&xstate); | ||
332 | xstate.bytes_in = zip_header->formatted.cmpsize; | ||
333 | xstate.src_fd = zip_fd; | ||
334 | xstate.dst_fd = dst_fd; | ||
335 | if (inflate_unzip(&xstate) < 0) | 358 | if (inflate_unzip(&xstate) < 0) |
336 | bb_error_msg_and_die("inflate error"); | 359 | bb_error_msg_and_die("inflate error"); |
337 | /* Validate decompression - crc */ | 360 | /* Validate decompression - crc */ |
338 | if (zip_header->formatted.crc32 != (xstate.crc32 ^ 0xffffffffL)) { | 361 | if (zip->fmt.crc32 != (xstate.crc32 ^ 0xffffffffL)) { |
339 | bb_error_msg_and_die("crc error"); | 362 | bb_error_msg_and_die("crc error"); |
340 | } | 363 | } |
341 | /* Validate decompression - size */ | ||
342 | if (zip_header->formatted.ucmpsize != xstate.bytes_out) { | ||
343 | /* Don't die. Who knows, maybe len calculation | ||
344 | * was botched somewhere. After all, crc matched! */ | ||
345 | bb_error_msg("bad length"); | ||
346 | } | ||
347 | } | 364 | } |
348 | /* TODO? method 12: bzip2, method 14: LZMA */ | 365 | #if ENABLE_FEATURE_UNZIP_BZIP2 |
366 | else if (zip->fmt.method == 12) { | ||
367 | /* Tested. Unpacker reads too much, but we use CDF | ||
368 | * and will seek to the correct beginning of next file. | ||
369 | */ | ||
370 | xstate.bytes_out = unpack_bz2_stream(&xstate); | ||
371 | if (xstate.bytes_out < 0) | ||
372 | bb_error_msg_and_die("inflate error"); | ||
373 | } | ||
374 | #endif | ||
375 | #if ENABLE_FEATURE_UNZIP_LZMA | ||
376 | else if (zip->fmt.method == 14) { | ||
377 | /* Not tested yet */ | ||
378 | xstate.bytes_out = unpack_lzma_stream(&xstate); | ||
379 | if (xstate.bytes_out < 0) | ||
380 | bb_error_msg_and_die("inflate error"); | ||
381 | } | ||
382 | #endif | ||
383 | #if ENABLE_FEATURE_UNZIP_XZ | ||
384 | else if (zip->fmt.method == 95) { | ||
385 | /* Not tested yet */ | ||
386 | xstate.bytes_out = unpack_xz_stream(&xstate); | ||
387 | if (xstate.bytes_out < 0) | ||
388 | bb_error_msg_and_die("inflate error"); | ||
389 | } | ||
390 | #endif | ||
391 | else { | ||
392 | bb_error_msg_and_die("unsupported method %u", zip->fmt.method); | ||
393 | } | ||
394 | |||
395 | /* Validate decompression - size */ | ||
396 | if (zip->fmt.ucmpsize != xstate.bytes_out) { | ||
397 | /* Don't die. Who knows, maybe len calculation | ||
398 | * was botched somewhere. After all, crc matched! */ | ||
399 | bb_error_msg("bad length"); | ||
400 | } | ||
349 | } | 401 | } |
350 | 402 | ||
351 | static void my_fgets80(char *buf80) | 403 | static void my_fgets80(char *buf80) |
@@ -566,7 +618,7 @@ int unzip_main(int argc, char **argv) | |||
566 | total_entries = 0; | 618 | total_entries = 0; |
567 | cdf_offset = find_cdf_offset(); /* try to seek to the end, find CDE and CDF start */ | 619 | cdf_offset = find_cdf_offset(); /* try to seek to the end, find CDE and CDF start */ |
568 | while (1) { | 620 | while (1) { |
569 | zip_header_t zip_header; | 621 | zip_header_t zip; |
570 | mode_t dir_mode = 0777; | 622 | mode_t dir_mode = 0777; |
571 | #if ENABLE_FEATURE_UNZIP_CDF | 623 | #if ENABLE_FEATURE_UNZIP_CDF |
572 | mode_t file_mode = 0666; | 624 | mode_t file_mode = 0666; |
@@ -592,7 +644,7 @@ int unzip_main(int argc, char **argv) | |||
592 | 644 | ||
593 | /* Check magic number */ | 645 | /* Check magic number */ |
594 | xread(zip_fd, &magic, 4); | 646 | xread(zip_fd, &magic, 4); |
595 | /* Central directory? It's at the end, so exit */ | 647 | /* CDF item? Assume there are no more files, exit */ |
596 | if (magic == ZIP_CDF_MAGIC) { | 648 | if (magic == ZIP_CDF_MAGIC) { |
597 | dbg("got ZIP_CDF_MAGIC"); | 649 | dbg("got ZIP_CDF_MAGIC"); |
598 | break; | 650 | break; |
@@ -608,71 +660,70 @@ int unzip_main(int argc, char **argv) | |||
608 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); | 660 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); |
609 | dbg("got ZIP_FILEHEADER_MAGIC"); | 661 | dbg("got ZIP_FILEHEADER_MAGIC"); |
610 | 662 | ||
611 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); | 663 | xread(zip_fd, zip.raw, ZIP_HEADER_LEN); |
612 | FIX_ENDIANNESS_ZIP(zip_header); | 664 | FIX_ENDIANNESS_ZIP(zip); |
613 | if ((zip_header.formatted.method != 0) | 665 | if (zip.fmt.zip_flags & SWAP_LE16(0x0008)) { |
614 | && (zip_header.formatted.method != 8) | 666 | bb_error_msg_and_die("zip flag %s is not supported", |
615 | ) { | 667 | "8 (streaming)"); |
616 | /* TODO? method 12: bzip2, method 14: LZMA */ | ||
617 | bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); | ||
618 | } | ||
619 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { | ||
620 | bb_error_msg_and_die("zip flags 1 and 8 are not supported"); | ||
621 | } | 668 | } |
622 | } | 669 | } |
623 | #if ENABLE_FEATURE_UNZIP_CDF | 670 | #if ENABLE_FEATURE_UNZIP_CDF |
624 | else { | 671 | else { |
625 | /* cdf_offset is valid (and we know the file is seekable) */ | 672 | /* cdf_offset is valid (and we know the file is seekable) */ |
626 | cdf_header_t cdf_header; | 673 | cdf_header_t cdf; |
627 | cdf_offset = read_next_cdf(cdf_offset, &cdf_header); | 674 | cdf_offset = read_next_cdf(cdf_offset, &cdf); |
628 | if (cdf_offset == 0) /* EOF? */ | 675 | if (cdf_offset == 0) /* EOF? */ |
629 | break; | 676 | break; |
630 | # if 1 | 677 | # if 1 |
631 | xlseek(zip_fd, | 678 | xlseek(zip_fd, |
632 | SWAP_LE32(cdf_header.formatted.relative_offset_of_local_header) + 4, | 679 | SWAP_LE32(cdf.fmt.relative_offset_of_local_header) + 4, |
633 | SEEK_SET); | 680 | SEEK_SET); |
634 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); | 681 | xread(zip_fd, zip.raw, ZIP_HEADER_LEN); |
635 | FIX_ENDIANNESS_ZIP(zip_header); | 682 | FIX_ENDIANNESS_ZIP(zip); |
636 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { | 683 | if (zip.fmt.zip_flags & SWAP_LE16(0x0008)) { |
637 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten | 684 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten |
638 | * only from Central Directory. | 685 | * only from Central Directory. |
639 | */ | 686 | */ |
640 | zip_header.formatted.crc32 = cdf_header.formatted.crc32; | 687 | zip.fmt.crc32 = cdf.fmt.crc32; |
641 | zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; | 688 | zip.fmt.cmpsize = cdf.fmt.cmpsize; |
642 | zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; | 689 | zip.fmt.ucmpsize = cdf.fmt.ucmpsize; |
643 | } | 690 | } |
644 | # else | 691 | # else |
645 | /* CDF has the same data as local header, no need to read the latter */ | 692 | /* CDF has the same data as local header, no need to read the latter... |
646 | memcpy(&zip_header.formatted.version, | 693 | * ...not really. An archive was seen with cdf.extra_len == 6 but |
647 | &cdf_header.formatted.version_needed, ZIP_HEADER_LEN); | 694 | * zip.extra_len == 0. |
695 | */ | ||
696 | memcpy(&zip.fmt.version, | ||
697 | &cdf.fmt.version_needed, ZIP_HEADER_LEN); | ||
648 | xlseek(zip_fd, | 698 | xlseek(zip_fd, |
649 | SWAP_LE32(cdf_header.formatted.relative_offset_of_local_header) + 4 + ZIP_HEADER_LEN, | 699 | SWAP_LE32(cdf.fmt.relative_offset_of_local_header) + 4 + ZIP_HEADER_LEN, |
650 | SEEK_SET); | 700 | SEEK_SET); |
651 | # endif | 701 | # endif |
652 | if ((cdf_header.formatted.version_made_by >> 8) == 3) { | 702 | if ((cdf.fmt.version_made_by >> 8) == 3) { |
653 | /* This archive is created on Unix */ | 703 | /* This archive is created on Unix */ |
654 | dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); | 704 | dir_mode = file_mode = (cdf.fmt.external_attributes >> 16); |
655 | } | 705 | } |
656 | } | 706 | } |
657 | #endif | 707 | #endif |
658 | 708 | ||
659 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { | 709 | if (zip.fmt.zip_flags & SWAP_LE16(0x0001)) { |
660 | /* 0x0001 - encrypted */ | 710 | /* 0x0001 - encrypted */ |
661 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); | 711 | bb_error_msg_and_die("zip flag %s is not supported", |
712 | "1 (encryption)"); | ||
662 | } | 713 | } |
663 | dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", | 714 | dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", |
664 | (unsigned)zip_header.formatted.cmpsize, | 715 | (unsigned)zip.fmt.cmpsize, |
665 | (unsigned)zip_header.formatted.extra_len, | 716 | (unsigned)zip.fmt.extra_len, |
666 | (unsigned)zip_header.formatted.ucmpsize | 717 | (unsigned)zip.fmt.ucmpsize |
667 | ); | 718 | ); |
668 | 719 | ||
669 | /* Read filename */ | 720 | /* Read filename */ |
670 | free(dst_fn); | 721 | free(dst_fn); |
671 | dst_fn = xzalloc(zip_header.formatted.filename_len + 1); | 722 | dst_fn = xzalloc(zip.fmt.filename_len + 1); |
672 | xread(zip_fd, dst_fn, zip_header.formatted.filename_len); | 723 | xread(zip_fd, dst_fn, zip.fmt.filename_len); |
673 | 724 | ||
674 | /* Skip extra header bytes */ | 725 | /* Skip extra header bytes */ |
675 | unzip_skip(zip_header.formatted.extra_len); | 726 | unzip_skip(zip.fmt.extra_len); |
676 | 727 | ||
677 | /* Guard against "/abspath", "/../" and similar attacks */ | 728 | /* Guard against "/abspath", "/../" and similar attacks */ |
678 | overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn)); | 729 | overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn)); |
@@ -681,129 +732,129 @@ int unzip_main(int argc, char **argv) | |||
681 | if (find_list_entry(zreject, dst_fn) | 732 | if (find_list_entry(zreject, dst_fn) |
682 | || (zaccept && !find_list_entry(zaccept, dst_fn)) | 733 | || (zaccept && !find_list_entry(zaccept, dst_fn)) |
683 | ) { /* Skip entry */ | 734 | ) { /* Skip entry */ |
684 | i = 'n'; | 735 | goto skip_cmpsize; |
685 | } else { | 736 | } |
686 | if (listing) { | 737 | |
687 | /* List entry */ | 738 | if (listing) { |
688 | char dtbuf[sizeof("mm-dd-yyyy hh:mm")]; | 739 | /* List entry */ |
689 | sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u", | 740 | char dtbuf[sizeof("mm-dd-yyyy hh:mm")]; |
690 | (zip_header.formatted.moddate >> 5) & 0xf, // mm: 0x01e0 | 741 | sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u", |
691 | (zip_header.formatted.moddate) & 0x1f, // dd: 0x001f | 742 | (zip.fmt.moddate >> 5) & 0xf, // mm: 0x01e0 |
692 | (zip_header.formatted.moddate >> 9) + 1980, // yy: 0xfe00 | 743 | (zip.fmt.moddate) & 0x1f, // dd: 0x001f |
693 | (zip_header.formatted.modtime >> 11), // hh: 0xf800 | 744 | (zip.fmt.moddate >> 9) + 1980, // yy: 0xfe00 |
694 | (zip_header.formatted.modtime >> 5) & 0x3f // mm: 0x07e0 | 745 | (zip.fmt.modtime >> 11), // hh: 0xf800 |
695 | // seconds/2 are not shown, encoded in ----------- 0x001f | 746 | (zip.fmt.modtime >> 5) & 0x3f // mm: 0x07e0 |
696 | ); | 747 | // seconds/2 not shown, encoded in -- 0x001f |
697 | if (!verbose) { | 748 | ); |
698 | // " Length Date Time Name\n" | 749 | if (!verbose) { |
699 | // "--------- ---------- ----- ----" | 750 | // " Length Date Time Name\n" |
700 | printf( "%9u " "%s " "%s\n", | 751 | // "--------- ---------- ----- ----" |
701 | (unsigned)zip_header.formatted.ucmpsize, | 752 | printf( "%9u " "%s " "%s\n", |
702 | dtbuf, | 753 | (unsigned)zip.fmt.ucmpsize, |
703 | dst_fn); | 754 | dtbuf, |
704 | } else { | 755 | dst_fn); |
705 | unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize; | 756 | } else { |
706 | if ((int32_t)percents < 0) | 757 | char method6[7]; |
707 | percents = 0; /* happens if ucmpsize < cmpsize */ | 758 | unsigned long percents; |
708 | percents = percents * 100; | 759 | |
709 | if (zip_header.formatted.ucmpsize) | 760 | sprintf(method6, "%6u", zip.fmt.method); |
710 | percents /= zip_header.formatted.ucmpsize; | 761 | if (zip.fmt.method == 0) { |
711 | // " Length Method Size Cmpr Date Time CRC-32 Name\n" | 762 | strcpy(method6, "Stored"); |
712 | // "-------- ------ ------- ---- ---------- ----- -------- ----" | 763 | } |
713 | printf( "%8u %s" "%9u%4u%% " "%s " "%08x " "%s\n", | 764 | if (zip.fmt.method == 8) { |
714 | (unsigned)zip_header.formatted.ucmpsize, | 765 | strcpy(method6, "Defl:N"); |
715 | zip_header.formatted.method == 0 ? "Stored" : "Defl:N", /* Defl is method 8 */ | 766 | /* normal, maximum, fast, superfast */ |
716 | /* TODO: show other methods? | 767 | IF_DESKTOP(method6[5] = "NXFS"[(zip.fmt.zip_flags >> 1) & 3];) |
717 | * 1 - Shrunk | ||
718 | * 2 - Reduced with compression factor 1 | ||
719 | * 3 - Reduced with compression factor 2 | ||
720 | * 4 - Reduced with compression factor 3 | ||
721 | * 5 - Reduced with compression factor 4 | ||
722 | * 6 - Imploded | ||
723 | * 7 - Reserved for Tokenizing compression algorithm | ||
724 | * 9 - Deflate64 | ||
725 | * 10 - PKWARE Data Compression Library Imploding | ||
726 | * 11 - Reserved by PKWARE | ||
727 | * 12 - BZIP2 | ||
728 | */ | ||
729 | (unsigned)zip_header.formatted.cmpsize, | ||
730 | (unsigned)percents, | ||
731 | dtbuf, | ||
732 | zip_header.formatted.crc32, | ||
733 | dst_fn); | ||
734 | total_size += zip_header.formatted.cmpsize; | ||
735 | } | 768 | } |
736 | total_usize += zip_header.formatted.ucmpsize; | 769 | percents = zip.fmt.ucmpsize - zip.fmt.cmpsize; |
737 | i = 'n'; | 770 | if ((int32_t)percents < 0) |
738 | } else if (dst_fd == STDOUT_FILENO) { | 771 | percents = 0; /* happens if ucmpsize < cmpsize */ |
739 | /* Extracting to STDOUT */ | 772 | percents = percents * 100; |
740 | i = -1; | 773 | if (zip.fmt.ucmpsize) |
741 | } else if (last_char_is(dst_fn, '/')) { | 774 | percents /= zip.fmt.ucmpsize; |
742 | /* Extract directory */ | 775 | // " Length Method Size Cmpr Date Time CRC-32 Name\n" |
743 | if (stat(dst_fn, &stat_buf) == -1) { | 776 | // "-------- ------ ------- ---- ---------- ----- -------- ----" |
744 | if (errno != ENOENT) { | 777 | printf( "%8u %s" "%9u%4u%% " "%s " "%08x " "%s\n", |
745 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); | 778 | (unsigned)zip.fmt.ucmpsize, |
746 | } | 779 | method6, |
747 | if (!quiet) { | 780 | (unsigned)zip.fmt.cmpsize, |
748 | printf(" creating: %s\n", dst_fn); | 781 | (unsigned)percents, |
749 | } | 782 | dtbuf, |
750 | unzip_create_leading_dirs(dst_fn); | 783 | zip.fmt.crc32, |
751 | if (bb_make_directory(dst_fn, dir_mode, FILEUTILS_IGNORE_CHMOD_ERR)) { | 784 | dst_fn); |
752 | xfunc_die(); | 785 | total_size += zip.fmt.cmpsize; |
753 | } | 786 | } |
754 | } else { | 787 | total_usize += zip.fmt.ucmpsize; |
755 | if (!S_ISDIR(stat_buf.st_mode)) { | 788 | goto skip_cmpsize; |
756 | bb_error_msg_and_die("'%s' exists but is not a %s", | 789 | } |
757 | dst_fn, "directory"); | 790 | |
758 | } | 791 | if (dst_fd == STDOUT_FILENO) { |
792 | /* Extracting to STDOUT */ | ||
793 | goto do_extract; | ||
794 | } | ||
795 | if (last_char_is(dst_fn, '/')) { | ||
796 | /* Extract directory */ | ||
797 | if (stat(dst_fn, &stat_buf) == -1) { | ||
798 | if (errno != ENOENT) { | ||
799 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); | ||
800 | } | ||
801 | if (!quiet) { | ||
802 | printf(" creating: %s\n", dst_fn); | ||
803 | } | ||
804 | unzip_create_leading_dirs(dst_fn); | ||
805 | if (bb_make_directory(dst_fn, dir_mode, FILEUTILS_IGNORE_CHMOD_ERR)) { | ||
806 | xfunc_die(); | ||
759 | } | 807 | } |
760 | i = 'n'; | ||
761 | } else { | 808 | } else { |
762 | /* Extract file */ | 809 | if (!S_ISDIR(stat_buf.st_mode)) { |
763 | check_file: | 810 | bb_error_msg_and_die("'%s' exists but is not a %s", |
764 | if (stat(dst_fn, &stat_buf) == -1) { | 811 | dst_fn, "directory"); |
765 | /* File does not exist */ | ||
766 | if (errno != ENOENT) { | ||
767 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); | ||
768 | } | ||
769 | i = 'y'; | ||
770 | } else { | ||
771 | /* File already exists */ | ||
772 | if (overwrite == O_NEVER) { | ||
773 | i = 'n'; | ||
774 | } else if (S_ISREG(stat_buf.st_mode)) { | ||
775 | /* File is regular file */ | ||
776 | if (overwrite == O_ALWAYS) { | ||
777 | i = 'y'; | ||
778 | } else { | ||
779 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); | ||
780 | my_fgets80(key_buf); | ||
781 | i = key_buf[0]; | ||
782 | } | ||
783 | } else { | ||
784 | /* File is not regular file */ | ||
785 | bb_error_msg_and_die("'%s' exists but is not a %s", | ||
786 | dst_fn, "regular file"); | ||
787 | } | ||
788 | } | 812 | } |
789 | } | 813 | } |
814 | goto skip_cmpsize; | ||
815 | } | ||
816 | check_file: | ||
817 | /* Extract file */ | ||
818 | if (stat(dst_fn, &stat_buf) == -1) { | ||
819 | /* File does not exist */ | ||
820 | if (errno != ENOENT) { | ||
821 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); | ||
822 | } | ||
823 | goto do_open_and_extract; | ||
824 | } | ||
825 | /* File already exists */ | ||
826 | if (overwrite == O_NEVER) { | ||
827 | goto skip_cmpsize; | ||
828 | } | ||
829 | if (!S_ISREG(stat_buf.st_mode)) { | ||
830 | /* File is not regular file */ | ||
831 | bb_error_msg_and_die("'%s' exists but is not a %s", | ||
832 | dst_fn, "regular file"); | ||
790 | } | 833 | } |
834 | /* File is regular file */ | ||
835 | if (overwrite == O_ALWAYS) | ||
836 | goto do_open_and_extract; | ||
837 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); | ||
838 | my_fgets80(key_buf); | ||
791 | 839 | ||
792 | switch (i) { | 840 | switch (key_buf[0]) { |
793 | case 'A': | 841 | case 'A': |
794 | overwrite = O_ALWAYS; | 842 | overwrite = O_ALWAYS; |
795 | case 'y': /* Open file and fall into unzip */ | 843 | case 'y': /* Open file and fall into unzip */ |
844 | do_open_and_extract: | ||
796 | unzip_create_leading_dirs(dst_fn); | 845 | unzip_create_leading_dirs(dst_fn); |
797 | #if ENABLE_FEATURE_UNZIP_CDF | 846 | #if ENABLE_FEATURE_UNZIP_CDF |
798 | dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); | 847 | dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); |
799 | #else | 848 | #else |
800 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); | 849 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); |
801 | #endif | 850 | #endif |
802 | case -1: /* Unzip */ | 851 | do_extract: |
803 | if (!quiet) { | 852 | if (!quiet) { |
804 | printf(" inflating: %s\n", dst_fn); | 853 | printf(/* zip.fmt.method == 0 |
854 | ? " extracting: %s\n" | ||
855 | : */ " inflating: %s\n", dst_fn); | ||
805 | } | 856 | } |
806 | unzip_extract(&zip_header, dst_fd); | 857 | unzip_extract(&zip, dst_fd); |
807 | if (dst_fd != STDOUT_FILENO) { | 858 | if (dst_fd != STDOUT_FILENO) { |
808 | /* closing STDOUT is potentially bad for future business */ | 859 | /* closing STDOUT is potentially bad for future business */ |
809 | close(dst_fd); | 860 | close(dst_fd); |
@@ -812,9 +863,9 @@ int unzip_main(int argc, char **argv) | |||
812 | 863 | ||
813 | case 'N': | 864 | case 'N': |
814 | overwrite = O_NEVER; | 865 | overwrite = O_NEVER; |
815 | case 'n': | 866 | case 'n': /* Skip entry data */ |
816 | /* Skip entry data */ | 867 | skip_cmpsize: |
817 | unzip_skip(zip_header.formatted.cmpsize); | 868 | unzip_skip(zip.fmt.cmpsize); |
818 | break; | 869 | break; |
819 | 870 | ||
820 | case 'r': | 871 | case 'r': |
@@ -827,7 +878,7 @@ int unzip_main(int argc, char **argv) | |||
827 | goto check_file; | 878 | goto check_file; |
828 | 879 | ||
829 | default: | 880 | default: |
830 | printf("error: invalid response [%c]\n", (char)i); | 881 | printf("error: invalid response [%c]\n", (char)key_buf[0]); |
831 | goto check_file; | 882 | goto check_file; |
832 | } | 883 | } |
833 | 884 | ||