diff options
author | Ron Yorston <rmy@pobox.com> | 2017-01-08 08:56:43 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-01-08 08:56:43 +0000 |
commit | 3ef86d069577b8a44ebe3aa890c6e97ea31d0d56 (patch) | |
tree | 064587c9b2080dba963bf8d93861b8019cb306ed /archival/unzip.c | |
parent | c66975af0b5335b9cdd156206767756237bd814b (diff) | |
parent | 86584e134eec1a81298149f8c04c77727f6dccb9 (diff) | |
download | busybox-w32-3ef86d069577b8a44ebe3aa890c6e97ea31d0d56.tar.gz busybox-w32-3ef86d069577b8a44ebe3aa890c6e97ea31d0d56.tar.bz2 busybox-w32-3ef86d069577b8a44ebe3aa890c6e97ea31d0d56.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'archival/unzip.c')
-rw-r--r-- | archival/unzip.c | 286 |
1 files changed, 163 insertions, 123 deletions
diff --git a/archival/unzip.c b/archival/unzip.c index 27adb3420..7029f66c3 100644 --- a/archival/unzip.c +++ b/archival/unzip.c | |||
@@ -16,7 +16,6 @@ | |||
16 | * TODO | 16 | * TODO |
17 | * Zip64 + other methods | 17 | * Zip64 + other methods |
18 | */ | 18 | */ |
19 | |||
20 | //config:config UNZIP | 19 | //config:config UNZIP |
21 | //config: bool "unzip" | 20 | //config: bool "unzip" |
22 | //config: default y | 21 | //config: default y |
@@ -24,8 +23,17 @@ | |||
24 | //config: unzip will list or extract files from a ZIP archive, | 23 | //config: unzip will list or extract files from a ZIP archive, |
25 | //config: commonly found on DOS/WIN systems. The default behavior | 24 | //config: commonly found on DOS/WIN systems. The default behavior |
26 | //config: (with no options) is to extract the archive into the | 25 | //config: (with no options) is to extract the archive into the |
27 | //config: current directory. Use the `-d' option to extract to a | 26 | //config: current directory. |
28 | //config: directory of your choice. | 27 | //config: |
28 | //config:config FEATURE_UNZIP_CDF | ||
29 | //config: bool "Read and use Central Directory data" | ||
30 | //config: default y | ||
31 | //config: depends on UNZIP | ||
32 | //config: help | ||
33 | //config: If you know that you only need to deal with simple | ||
34 | //config: ZIP files without deleted/updated files, SFX archives etc, | ||
35 | //config: you can reduce code size by unselecting this option. | ||
36 | //config: To support less trivial ZIPs, say Y. | ||
29 | 37 | ||
30 | //applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP)) | 38 | //applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP)) |
31 | //kbuild:lib-$(CONFIG_UNZIP) += unzip.o | 39 | //kbuild:lib-$(CONFIG_UNZIP) += unzip.o |
@@ -83,30 +91,20 @@ typedef union { | |||
83 | uint32_t ucmpsize PACKED; /* 18-21 */ | 91 | uint32_t ucmpsize PACKED; /* 18-21 */ |
84 | uint16_t filename_len; /* 22-23 */ | 92 | uint16_t filename_len; /* 22-23 */ |
85 | uint16_t extra_len; /* 24-25 */ | 93 | uint16_t extra_len; /* 24-25 */ |
94 | /* filename follows (not NUL terminated) */ | ||
95 | /* extra field follows */ | ||
96 | /* data follows */ | ||
86 | } formatted PACKED; | 97 | } formatted PACKED; |
87 | } zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ | 98 | } zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ |
88 | 99 | ||
89 | /* Check the offset of the last element, not the length. This leniency | 100 | #define FIX_ENDIANNESS_ZIP(zip_header) \ |
90 | * allows for poor packing, whereby the overall struct may be too long, | 101 | do { if (BB_BIG_ENDIAN) { \ |
91 | * even though the elements are all in the right place. | ||
92 | */ | ||
93 | struct BUG_zip_header_must_be_26_bytes { | ||
94 | char BUG_zip_header_must_be_26_bytes[ | ||
95 | offsetof(zip_header_t, formatted.extra_len) + 2 | ||
96 | == ZIP_HEADER_LEN ? 1 : -1]; | ||
97 | }; | ||
98 | |||
99 | #define FIX_ENDIANNESS_ZIP(zip_header) do { \ | ||
100 | (zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ | ||
101 | (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ | ||
102 | (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ | ||
103 | (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ | ||
104 | (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ | 102 | (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ |
105 | (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ | 103 | (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ |
106 | (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ | 104 | (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ |
107 | (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ | 105 | (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ |
108 | (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ | 106 | (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ |
109 | } while (0) | 107 | }} while (0) |
110 | 108 | ||
111 | #define CDF_HEADER_LEN 42 | 109 | #define CDF_HEADER_LEN 42 |
112 | 110 | ||
@@ -118,8 +116,8 @@ typedef union { | |||
118 | uint16_t version_needed; /* 2-3 */ | 116 | uint16_t version_needed; /* 2-3 */ |
119 | uint16_t cdf_flags; /* 4-5 */ | 117 | uint16_t cdf_flags; /* 4-5 */ |
120 | uint16_t method; /* 6-7 */ | 118 | uint16_t method; /* 6-7 */ |
121 | uint16_t mtime; /* 8-9 */ | 119 | uint16_t modtime; /* 8-9 */ |
122 | uint16_t mdate; /* 10-11 */ | 120 | uint16_t moddate; /* 10-11 */ |
123 | uint32_t crc32; /* 12-15 */ | 121 | uint32_t crc32; /* 12-15 */ |
124 | uint32_t cmpsize; /* 16-19 */ | 122 | uint32_t cmpsize; /* 16-19 */ |
125 | uint32_t ucmpsize; /* 20-23 */ | 123 | uint32_t ucmpsize; /* 20-23 */ |
@@ -130,27 +128,27 @@ typedef union { | |||
130 | uint16_t internal_file_attributes; /* 32-33 */ | 128 | uint16_t internal_file_attributes; /* 32-33 */ |
131 | uint32_t external_file_attributes PACKED; /* 34-37 */ | 129 | uint32_t external_file_attributes PACKED; /* 34-37 */ |
132 | uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ | 130 | uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ |
131 | /* filename follows (not NUL terminated) */ | ||
132 | /* extra field follows */ | ||
133 | /* comment follows */ | ||
133 | } formatted PACKED; | 134 | } formatted PACKED; |
134 | } cdf_header_t; | 135 | } cdf_header_t; |
135 | 136 | ||
136 | struct BUG_cdf_header_must_be_42_bytes { | 137 | #define FIX_ENDIANNESS_CDF(cdf_header) \ |
137 | char BUG_cdf_header_must_be_42_bytes[ | 138 | do { if (BB_BIG_ENDIAN) { \ |
138 | offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 | 139 | (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ |
139 | == CDF_HEADER_LEN ? 1 : -1]; | 140 | (cdf_header).formatted.version_needed = SWAP_LE16((cdf_header).formatted.version_needed); \ |
140 | }; | 141 | (cdf_header).formatted.method = SWAP_LE16((cdf_header).formatted.method ); \ |
141 | 142 | (cdf_header).formatted.modtime = SWAP_LE16((cdf_header).formatted.modtime ); \ | |
142 | #define FIX_ENDIANNESS_CDF(cdf_header) do { \ | 143 | (cdf_header).formatted.moddate = SWAP_LE16((cdf_header).formatted.moddate ); \ |
143 | (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ | 144 | (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ |
144 | (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ | 145 | (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ |
145 | (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ | 146 | (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ |
146 | (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ | 147 | (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ |
147 | (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ | 148 | (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ |
148 | (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \ | 149 | (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \ |
149 | IF_DESKTOP( \ | ||
150 | (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ | ||
151 | (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \ | 150 | (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \ |
152 | ) \ | 151 | }} while (0) |
153 | } while (0) | ||
154 | 152 | ||
155 | #define CDE_HEADER_LEN 16 | 153 | #define CDE_HEADER_LEN 16 |
156 | 154 | ||
@@ -169,20 +167,38 @@ typedef union { | |||
169 | } formatted PACKED; | 167 | } formatted PACKED; |
170 | } cde_header_t; | 168 | } cde_header_t; |
171 | 169 | ||
172 | struct BUG_cde_header_must_be_16_bytes { | 170 | #define FIX_ENDIANNESS_CDE(cde_header) \ |
171 | do { if (BB_BIG_ENDIAN) { \ | ||
172 | (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ | ||
173 | }} while (0) | ||
174 | |||
175 | struct BUG { | ||
176 | /* Check the offset of the last element, not the length. This leniency | ||
177 | * allows for poor packing, whereby the overall struct may be too long, | ||
178 | * even though the elements are all in the right place. | ||
179 | */ | ||
180 | char BUG_zip_header_must_be_26_bytes[ | ||
181 | offsetof(zip_header_t, formatted.extra_len) + 2 | ||
182 | == ZIP_HEADER_LEN ? 1 : -1]; | ||
183 | char BUG_cdf_header_must_be_42_bytes[ | ||
184 | offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 | ||
185 | == CDF_HEADER_LEN ? 1 : -1]; | ||
173 | char BUG_cde_header_must_be_16_bytes[ | 186 | char BUG_cde_header_must_be_16_bytes[ |
174 | sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; | 187 | sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; |
175 | }; | 188 | }; |
176 | 189 | ||
177 | #define FIX_ENDIANNESS_CDE(cde_header) do { \ | ||
178 | (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ | ||
179 | } while (0) | ||
180 | 190 | ||
181 | enum { zip_fd = 3 }; | 191 | enum { zip_fd = 3 }; |
182 | 192 | ||
183 | 193 | ||
184 | #if ENABLE_DESKTOP | 194 | /* This value means that we failed to find CDF */ |
195 | #define BAD_CDF_OFFSET ((uint32_t)0xffffffff) | ||
196 | |||
197 | #if !ENABLE_FEATURE_UNZIP_CDF | ||
198 | |||
199 | # define find_cdf_offset() BAD_CDF_OFFSET | ||
185 | 200 | ||
201 | #else | ||
186 | /* Seen in the wild: | 202 | /* Seen in the wild: |
187 | * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive, | 203 | * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive, |
188 | * where CDE was nearly 48 kbytes before EOF. | 204 | * where CDE was nearly 48 kbytes before EOF. |
@@ -191,25 +207,26 @@ enum { zip_fd = 3 }; | |||
191 | * To make extraction work, bumped PEEK_FROM_END from 16k to 64k. | 207 | * To make extraction work, bumped PEEK_FROM_END from 16k to 64k. |
192 | */ | 208 | */ |
193 | #define PEEK_FROM_END (64*1024) | 209 | #define PEEK_FROM_END (64*1024) |
194 | |||
195 | /* This value means that we failed to find CDF */ | ||
196 | #define BAD_CDF_OFFSET ((uint32_t)0xffffffff) | ||
197 | |||
198 | /* NB: does not preserve file position! */ | 210 | /* NB: does not preserve file position! */ |
199 | static uint32_t find_cdf_offset(void) | 211 | static uint32_t find_cdf_offset(void) |
200 | { | 212 | { |
201 | cde_header_t cde_header; | 213 | cde_header_t cde_header; |
214 | unsigned char *buf; | ||
202 | unsigned char *p; | 215 | unsigned char *p; |
203 | off_t end; | 216 | off_t end; |
204 | unsigned char *buf = xzalloc(PEEK_FROM_END); | ||
205 | uint32_t found; | 217 | uint32_t found; |
206 | 218 | ||
207 | end = xlseek(zip_fd, 0, SEEK_END); | 219 | end = lseek(zip_fd, 0, SEEK_END); |
220 | if (end == (off_t) -1) | ||
221 | return BAD_CDF_OFFSET; | ||
222 | |||
208 | end -= PEEK_FROM_END; | 223 | end -= PEEK_FROM_END; |
209 | if (end < 0) | 224 | if (end < 0) |
210 | end = 0; | 225 | end = 0; |
226 | |||
211 | dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end); | 227 | dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end); |
212 | xlseek(zip_fd, end, SEEK_SET); | 228 | xlseek(zip_fd, end, SEEK_SET); |
229 | buf = xzalloc(PEEK_FROM_END); | ||
213 | full_read(zip_fd, buf, PEEK_FROM_END); | 230 | full_read(zip_fd, buf, PEEK_FROM_END); |
214 | 231 | ||
215 | found = BAD_CDF_OFFSET; | 232 | found = BAD_CDF_OFFSET; |
@@ -254,31 +271,32 @@ static uint32_t find_cdf_offset(void) | |||
254 | 271 | ||
255 | static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) | 272 | static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) |
256 | { | 273 | { |
257 | off_t org; | 274 | uint32_t magic; |
258 | 275 | ||
259 | org = xlseek(zip_fd, 0, SEEK_CUR); | 276 | if (cdf_offset == BAD_CDF_OFFSET) |
260 | 277 | return cdf_offset; | |
261 | if (!cdf_offset) | 278 | |
262 | cdf_offset = find_cdf_offset(); | 279 | dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); |
263 | 280 | xlseek(zip_fd, cdf_offset, SEEK_SET); | |
264 | if (cdf_offset != BAD_CDF_OFFSET) { | 281 | xread(zip_fd, &magic, 4); |
265 | dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); | 282 | /* Central Directory End? */ |
266 | xlseek(zip_fd, cdf_offset + 4, SEEK_SET); | 283 | if (magic == ZIP_CDE_MAGIC) { |
267 | xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); | 284 | dbg("got ZIP_CDE_MAGIC"); |
268 | FIX_ENDIANNESS_CDF(*cdf_ptr); | 285 | return 0; /* EOF */ |
269 | dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u", | ||
270 | (unsigned)cdf_ptr->formatted.file_name_length, | ||
271 | (unsigned)cdf_ptr->formatted.extra_field_length, | ||
272 | (unsigned)cdf_ptr->formatted.file_comment_length | ||
273 | ); | ||
274 | cdf_offset += 4 + CDF_HEADER_LEN | ||
275 | + cdf_ptr->formatted.file_name_length | ||
276 | + cdf_ptr->formatted.extra_field_length | ||
277 | + cdf_ptr->formatted.file_comment_length; | ||
278 | } | 286 | } |
287 | xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); | ||
288 | |||
289 | FIX_ENDIANNESS_CDF(*cdf_ptr); | ||
290 | dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u", | ||
291 | (unsigned)cdf_ptr->formatted.file_name_length, | ||
292 | (unsigned)cdf_ptr->formatted.extra_field_length, | ||
293 | (unsigned)cdf_ptr->formatted.file_comment_length | ||
294 | ); | ||
295 | cdf_offset += 4 + CDF_HEADER_LEN | ||
296 | + cdf_ptr->formatted.file_name_length | ||
297 | + cdf_ptr->formatted.extra_field_length | ||
298 | + cdf_ptr->formatted.file_comment_length; | ||
279 | 299 | ||
280 | dbg("Returning file position to 0x%"OFF_FMT"x", org); | ||
281 | xlseek(zip_fd, org, SEEK_SET); | ||
282 | return cdf_offset; | 300 | return cdf_offset; |
283 | }; | 301 | }; |
284 | #endif | 302 | #endif |
@@ -327,6 +345,7 @@ static void unzip_extract(zip_header_t *zip_header, int dst_fd) | |||
327 | bb_error_msg("bad length"); | 345 | bb_error_msg("bad length"); |
328 | } | 346 | } |
329 | } | 347 | } |
348 | /* TODO? method 12: bzip2, method 14: LZMA */ | ||
330 | } | 349 | } |
331 | 350 | ||
332 | static void my_fgets80(char *buf80) | 351 | static void my_fgets80(char *buf80) |
@@ -342,15 +361,12 @@ int unzip_main(int argc, char **argv) | |||
342 | { | 361 | { |
343 | enum { O_PROMPT, O_NEVER, O_ALWAYS }; | 362 | enum { O_PROMPT, O_NEVER, O_ALWAYS }; |
344 | 363 | ||
345 | zip_header_t zip_header; | ||
346 | smallint quiet = 0; | 364 | smallint quiet = 0; |
347 | IF_NOT_DESKTOP(const) smallint verbose = 0; | 365 | IF_NOT_FEATURE_UNZIP_CDF(const) smallint verbose = 0; |
348 | smallint listing = 0; | 366 | smallint listing = 0; |
349 | smallint overwrite = O_PROMPT; | 367 | smallint overwrite = O_PROMPT; |
350 | smallint x_opt_seen; | 368 | smallint x_opt_seen; |
351 | #if ENABLE_DESKTOP | ||
352 | uint32_t cdf_offset; | 369 | uint32_t cdf_offset; |
353 | #endif | ||
354 | unsigned long total_usize; | 370 | unsigned long total_usize; |
355 | unsigned long total_size; | 371 | unsigned long total_size; |
356 | unsigned total_entries; | 372 | unsigned total_entries; |
@@ -433,7 +449,7 @@ int unzip_main(int argc, char **argv) | |||
433 | break; | 449 | break; |
434 | 450 | ||
435 | case 'v': /* Verbose list */ | 451 | case 'v': /* Verbose list */ |
436 | IF_DESKTOP(verbose++;) | 452 | IF_FEATURE_UNZIP_CDF(verbose++;) |
437 | listing = 1; | 453 | listing = 1; |
438 | break; | 454 | break; |
439 | 455 | ||
@@ -548,78 +564,102 @@ int unzip_main(int argc, char **argv) | |||
548 | total_usize = 0; | 564 | total_usize = 0; |
549 | total_size = 0; | 565 | total_size = 0; |
550 | total_entries = 0; | 566 | total_entries = 0; |
551 | #if ENABLE_DESKTOP | 567 | cdf_offset = find_cdf_offset(); /* try to seek to the end, find CDE and CDF start */ |
552 | cdf_offset = 0; | ||
553 | #endif | ||
554 | while (1) { | 568 | while (1) { |
555 | uint32_t magic; | 569 | zip_header_t zip_header; |
556 | mode_t dir_mode = 0777; | 570 | mode_t dir_mode = 0777; |
557 | #if ENABLE_DESKTOP | 571 | #if ENABLE_FEATURE_UNZIP_CDF |
558 | mode_t file_mode = 0666; | 572 | mode_t file_mode = 0666; |
559 | #endif | 573 | #endif |
560 | 574 | ||
561 | /* Check magic number */ | 575 | if (!ENABLE_FEATURE_UNZIP_CDF || cdf_offset == BAD_CDF_OFFSET) { |
562 | xread(zip_fd, &magic, 4); | 576 | /* Normally happens when input is unseekable. |
563 | /* Central directory? It's at the end, so exit */ | 577 | * |
564 | if (magic == ZIP_CDF_MAGIC) { | 578 | * Valid ZIP file has Central Directory at the end |
565 | dbg("got ZIP_CDF_MAGIC"); | 579 | * with central directory file headers (CDFs). |
566 | break; | 580 | * After it, there is a Central Directory End structure. |
567 | } | 581 | * CDFs identify what files are in the ZIP and where |
568 | #if ENABLE_DESKTOP | 582 | * they are located. This allows ZIP readers to load |
569 | /* Data descriptor? It was a streaming file, go on */ | 583 | * the list of files without reading the entire ZIP archive. |
570 | if (magic == ZIP_DD_MAGIC) { | 584 | * ZIP files may be appended to, only files specified in |
571 | dbg("got ZIP_DD_MAGIC"); | 585 | * the CD are valid. Scanning for local file headers is |
572 | /* skip over duplicate crc32, cmpsize and ucmpsize */ | 586 | * not a correct algorithm. |
573 | unzip_skip(3 * 4); | 587 | * |
574 | continue; | 588 | * We try to do the above, and resort to "linear" reading |
575 | } | 589 | * of ZIP file only if seek failed or CDE wasn't found. |
576 | #endif | 590 | */ |
577 | if (magic != ZIP_FILEHEADER_MAGIC) | 591 | uint32_t magic; |
578 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); | ||
579 | dbg("got ZIP_FILEHEADER_MAGIC"); | ||
580 | |||
581 | /* Read the file header */ | ||
582 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); | ||
583 | FIX_ENDIANNESS_ZIP(zip_header); | ||
584 | if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { | ||
585 | bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); | ||
586 | } | ||
587 | #if !ENABLE_DESKTOP | ||
588 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { | ||
589 | bb_error_msg_and_die("zip flags 1 and 8 are not supported"); | ||
590 | } | ||
591 | #else | ||
592 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { | ||
593 | /* 0x0001 - encrypted */ | ||
594 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); | ||
595 | } | ||
596 | 592 | ||
597 | if (cdf_offset != BAD_CDF_OFFSET) { | 593 | /* Check magic number */ |
594 | xread(zip_fd, &magic, 4); | ||
595 | /* Central directory? It's at the end, so exit */ | ||
596 | if (magic == ZIP_CDF_MAGIC) { | ||
597 | dbg("got ZIP_CDF_MAGIC"); | ||
598 | break; | ||
599 | } | ||
600 | /* Data descriptor? It was a streaming file, go on */ | ||
601 | if (magic == ZIP_DD_MAGIC) { | ||
602 | dbg("got ZIP_DD_MAGIC"); | ||
603 | /* skip over duplicate crc32, cmpsize and ucmpsize */ | ||
604 | unzip_skip(3 * 4); | ||
605 | continue; | ||
606 | } | ||
607 | if (magic != ZIP_FILEHEADER_MAGIC) | ||
608 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); | ||
609 | dbg("got ZIP_FILEHEADER_MAGIC"); | ||
610 | |||
611 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); | ||
612 | FIX_ENDIANNESS_ZIP(zip_header); | ||
613 | if ((zip_header.formatted.method != 0) | ||
614 | && (zip_header.formatted.method != 8) | ||
615 | ) { | ||
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 | } | ||
622 | } | ||
623 | #if ENABLE_FEATURE_UNZIP_CDF | ||
624 | else { | ||
625 | /* cdf_offset is valid (and we know the file is seekable) */ | ||
598 | cdf_header_t cdf_header; | 626 | cdf_header_t cdf_header; |
599 | cdf_offset = read_next_cdf(cdf_offset, &cdf_header); | 627 | cdf_offset = read_next_cdf(cdf_offset, &cdf_header); |
600 | /* | 628 | if (cdf_offset == 0) /* EOF? */ |
601 | * Note: cdf_offset can become BAD_CDF_OFFSET after the above call. | 629 | break; |
602 | */ | 630 | # if 0 |
631 | xlseek(zip_fd, | ||
632 | SWAP_LE32(cdf_header.formatted.relative_offset_of_local_header) + 4, | ||
633 | SEEK_SET); | ||
634 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); | ||
635 | FIX_ENDIANNESS_ZIP(zip_header); | ||
603 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { | 636 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { |
604 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten | 637 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten |
605 | * only from Central Directory. See unzip_doc.txt | 638 | * only from Central Directory. |
606 | */ | 639 | */ |
607 | zip_header.formatted.crc32 = cdf_header.formatted.crc32; | 640 | zip_header.formatted.crc32 = cdf_header.formatted.crc32; |
608 | zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; | 641 | zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; |
609 | zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; | 642 | zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; |
610 | } | 643 | } |
644 | # else | ||
645 | /* CDF has the same data as local header, no need to read the latter */ | ||
646 | memcpy(&zip_header.formatted.version, | ||
647 | &cdf_header.formatted.version_needed, ZIP_HEADER_LEN); | ||
648 | xlseek(zip_fd, | ||
649 | SWAP_LE32(cdf_header.formatted.relative_offset_of_local_header) + 4 + ZIP_HEADER_LEN, | ||
650 | SEEK_SET); | ||
651 | # endif | ||
611 | if ((cdf_header.formatted.version_made_by >> 8) == 3) { | 652 | if ((cdf_header.formatted.version_made_by >> 8) == 3) { |
612 | /* This archive is created on Unix */ | 653 | /* This archive is created on Unix */ |
613 | dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); | 654 | dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); |
614 | } | 655 | } |
615 | } | 656 | } |
616 | if (cdf_offset == BAD_CDF_OFFSET | ||
617 | && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) | ||
618 | ) { | ||
619 | /* If it's a streaming zip, we _require_ CDF */ | ||
620 | bb_error_msg_and_die("can't find file table"); | ||
621 | } | ||
622 | #endif | 657 | #endif |
658 | |||
659 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { | ||
660 | /* 0x0001 - encrypted */ | ||
661 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); | ||
662 | } | ||
623 | dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", | 663 | dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", |
624 | (unsigned)zip_header.formatted.cmpsize, | 664 | (unsigned)zip_header.formatted.cmpsize, |
625 | (unsigned)zip_header.formatted.extra_len, | 665 | (unsigned)zip_header.formatted.extra_len, |
@@ -754,7 +794,7 @@ int unzip_main(int argc, char **argv) | |||
754 | overwrite = O_ALWAYS; | 794 | overwrite = O_ALWAYS; |
755 | case 'y': /* Open file and fall into unzip */ | 795 | case 'y': /* Open file and fall into unzip */ |
756 | unzip_create_leading_dirs(dst_fn); | 796 | unzip_create_leading_dirs(dst_fn); |
757 | #if ENABLE_DESKTOP | 797 | #if ENABLE_FEATURE_UNZIP_CDF |
758 | dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); | 798 | dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); |
759 | #else | 799 | #else |
760 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); | 800 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); |