Contents of /trunk/mkinitrd-magellan/busybox/archival/unzip.c
Parent Directory | Revision Log
Revision 1123 -
(show annotations)
(download)
Wed Aug 18 21:56:57 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 21566 byte(s)
Wed Aug 18 21:56:57 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 21566 byte(s)
-updated to busybox-1.17.1
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini unzip implementation for busybox |
4 | * |
5 | * Copyright (C) 2004 by Ed Clark |
6 | * |
7 | * Loosely based on original busybox unzip applet by Laurence Anderson. |
8 | * All options and features should work in this version. |
9 | * |
10 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
11 | */ |
12 | |
13 | /* For reference see |
14 | * http://www.pkware.com/company/standards/appnote/ |
15 | * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip |
16 | */ |
17 | |
18 | /* TODO |
19 | * Zip64 + other methods |
20 | */ |
21 | |
22 | #include "libbb.h" |
23 | #include "unarchive.h" |
24 | |
25 | enum { |
26 | #if BB_BIG_ENDIAN |
27 | ZIP_FILEHEADER_MAGIC = 0x504b0304, |
28 | ZIP_CDF_MAGIC = 0x504b0102, /* central directory's file header */ |
29 | ZIP_CDE_MAGIC = 0x504b0506, /* "end of central directory" record */ |
30 | ZIP_DD_MAGIC = 0x504b0708, |
31 | #else |
32 | ZIP_FILEHEADER_MAGIC = 0x04034b50, |
33 | ZIP_CDF_MAGIC = 0x02014b50, |
34 | ZIP_CDE_MAGIC = 0x06054b50, |
35 | ZIP_DD_MAGIC = 0x08074b50, |
36 | #endif |
37 | }; |
38 | |
39 | #define ZIP_HEADER_LEN 26 |
40 | |
41 | typedef union { |
42 | uint8_t raw[ZIP_HEADER_LEN]; |
43 | struct { |
44 | uint16_t version; /* 0-1 */ |
45 | uint16_t zip_flags; /* 2-3 */ |
46 | uint16_t method; /* 4-5 */ |
47 | uint16_t modtime; /* 6-7 */ |
48 | uint16_t moddate; /* 8-9 */ |
49 | uint32_t crc32 PACKED; /* 10-13 */ |
50 | uint32_t cmpsize PACKED; /* 14-17 */ |
51 | uint32_t ucmpsize PACKED; /* 18-21 */ |
52 | uint16_t filename_len; /* 22-23 */ |
53 | uint16_t extra_len; /* 24-25 */ |
54 | } formatted PACKED; |
55 | } zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ |
56 | |
57 | /* Check the offset of the last element, not the length. This leniency |
58 | * allows for poor packing, whereby the overall struct may be too long, |
59 | * even though the elements are all in the right place. |
60 | */ |
61 | struct BUG_zip_header_must_be_26_bytes { |
62 | char BUG_zip_header_must_be_26_bytes[ |
63 | offsetof(zip_header_t, formatted.extra_len) + 2 |
64 | == ZIP_HEADER_LEN ? 1 : -1]; |
65 | }; |
66 | |
67 | #define FIX_ENDIANNESS_ZIP(zip_header) do { \ |
68 | (zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ |
69 | (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ |
70 | (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ |
71 | (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ |
72 | (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ |
73 | (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ |
74 | (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ |
75 | (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ |
76 | (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ |
77 | } while (0) |
78 | |
79 | #define CDF_HEADER_LEN 42 |
80 | |
81 | typedef union { |
82 | uint8_t raw[CDF_HEADER_LEN]; |
83 | struct { |
84 | /* uint32_t signature; 50 4b 01 02 */ |
85 | uint16_t version_made_by; /* 0-1 */ |
86 | uint16_t version_needed; /* 2-3 */ |
87 | uint16_t cdf_flags; /* 4-5 */ |
88 | uint16_t method; /* 6-7 */ |
89 | uint16_t mtime; /* 8-9 */ |
90 | uint16_t mdate; /* 10-11 */ |
91 | uint32_t crc32; /* 12-15 */ |
92 | uint32_t cmpsize; /* 16-19 */ |
93 | uint32_t ucmpsize; /* 20-23 */ |
94 | uint16_t file_name_length; /* 24-25 */ |
95 | uint16_t extra_field_length; /* 26-27 */ |
96 | uint16_t file_comment_length; /* 28-29 */ |
97 | uint16_t disk_number_start; /* 30-31 */ |
98 | uint16_t internal_file_attributes; /* 32-33 */ |
99 | uint32_t external_file_attributes PACKED; /* 34-37 */ |
100 | uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ |
101 | } formatted PACKED; |
102 | } cdf_header_t; |
103 | |
104 | struct BUG_cdf_header_must_be_42_bytes { |
105 | char BUG_cdf_header_must_be_42_bytes[ |
106 | offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 |
107 | == CDF_HEADER_LEN ? 1 : -1]; |
108 | }; |
109 | |
110 | #define FIX_ENDIANNESS_CDF(cdf_header) do { \ |
111 | (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ |
112 | (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ |
113 | (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ |
114 | (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ |
115 | (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ |
116 | (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \ |
117 | IF_DESKTOP( \ |
118 | (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ |
119 | (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \ |
120 | ) \ |
121 | } while (0) |
122 | |
123 | #define CDE_HEADER_LEN 16 |
124 | |
125 | typedef union { |
126 | uint8_t raw[CDE_HEADER_LEN]; |
127 | struct { |
128 | /* uint32_t signature; 50 4b 05 06 */ |
129 | uint16_t this_disk_no; |
130 | uint16_t disk_with_cdf_no; |
131 | uint16_t cdf_entries_on_this_disk; |
132 | uint16_t cdf_entries_total; |
133 | uint32_t cdf_size; |
134 | uint32_t cdf_offset; |
135 | /* uint16_t file_comment_length; */ |
136 | /* .ZIP file comment (variable size) */ |
137 | } formatted PACKED; |
138 | } cde_header_t; |
139 | |
140 | struct BUG_cde_header_must_be_16_bytes { |
141 | char BUG_cde_header_must_be_16_bytes[ |
142 | sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; |
143 | }; |
144 | |
145 | #define FIX_ENDIANNESS_CDE(cde_header) do { \ |
146 | (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ |
147 | } while (0) |
148 | |
149 | enum { zip_fd = 3 }; |
150 | |
151 | |
152 | #if ENABLE_DESKTOP |
153 | |
154 | #define PEEK_FROM_END 16384 |
155 | |
156 | /* NB: does not preserve file position! */ |
157 | static uint32_t find_cdf_offset(void) |
158 | { |
159 | cde_header_t cde_header; |
160 | unsigned char *p; |
161 | off_t end; |
162 | unsigned char *buf = xzalloc(PEEK_FROM_END); |
163 | |
164 | end = xlseek(zip_fd, 0, SEEK_END); |
165 | end -= PEEK_FROM_END; |
166 | if (end < 0) |
167 | end = 0; |
168 | xlseek(zip_fd, end, SEEK_SET); |
169 | full_read(zip_fd, buf, PEEK_FROM_END); |
170 | |
171 | p = buf; |
172 | while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { |
173 | if (*p != 'P') { |
174 | p++; |
175 | continue; |
176 | } |
177 | if (*++p != 'K') |
178 | continue; |
179 | if (*++p != 5) |
180 | continue; |
181 | if (*++p != 6) |
182 | continue; |
183 | /* we found CDE! */ |
184 | memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); |
185 | FIX_ENDIANNESS_CDE(cde_header); |
186 | free(buf); |
187 | return cde_header.formatted.cdf_offset; |
188 | } |
189 | //free(buf); |
190 | bb_error_msg_and_die("can't find file table"); |
191 | }; |
192 | |
193 | static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) |
194 | { |
195 | off_t org; |
196 | |
197 | org = xlseek(zip_fd, 0, SEEK_CUR); |
198 | |
199 | if (!cdf_offset) |
200 | cdf_offset = find_cdf_offset(); |
201 | |
202 | xlseek(zip_fd, cdf_offset + 4, SEEK_SET); |
203 | xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); |
204 | FIX_ENDIANNESS_CDF(*cdf_ptr); |
205 | cdf_offset += 4 + CDF_HEADER_LEN |
206 | + cdf_ptr->formatted.file_name_length |
207 | + cdf_ptr->formatted.extra_field_length |
208 | + cdf_ptr->formatted.file_comment_length; |
209 | |
210 | xlseek(zip_fd, org, SEEK_SET); |
211 | return cdf_offset; |
212 | }; |
213 | #endif |
214 | |
215 | static void unzip_skip(off_t skip) |
216 | { |
217 | if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) |
218 | bb_copyfd_exact_size(zip_fd, -1, skip); |
219 | } |
220 | |
221 | static void unzip_create_leading_dirs(const char *fn) |
222 | { |
223 | /* Create all leading directories */ |
224 | char *name = xstrdup(fn); |
225 | if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) { |
226 | bb_error_msg_and_die("exiting"); /* bb_make_directory is noisy */ |
227 | } |
228 | free(name); |
229 | } |
230 | |
231 | static void unzip_extract(zip_header_t *zip_header, int dst_fd) |
232 | { |
233 | if (zip_header->formatted.method == 0) { |
234 | /* Method 0 - stored (not compressed) */ |
235 | off_t size = zip_header->formatted.ucmpsize; |
236 | if (size) |
237 | bb_copyfd_exact_size(zip_fd, dst_fd, size); |
238 | } else { |
239 | /* Method 8 - inflate */ |
240 | inflate_unzip_result res; |
241 | if (inflate_unzip(&res, zip_header->formatted.cmpsize, zip_fd, dst_fd) < 0) |
242 | bb_error_msg_and_die("inflate error"); |
243 | /* Validate decompression - crc */ |
244 | if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) { |
245 | bb_error_msg_and_die("crc error"); |
246 | } |
247 | /* Validate decompression - size */ |
248 | if (zip_header->formatted.ucmpsize != res.bytes_out) { |
249 | /* Don't die. Who knows, maybe len calculation |
250 | * was botched somewhere. After all, crc matched! */ |
251 | bb_error_msg("bad length"); |
252 | } |
253 | } |
254 | } |
255 | |
256 | int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
257 | int unzip_main(int argc, char **argv) |
258 | { |
259 | enum { O_PROMPT, O_NEVER, O_ALWAYS }; |
260 | |
261 | zip_header_t zip_header; |
262 | smallint quiet = 0; |
263 | IF_NOT_DESKTOP(const) smallint verbose = 0; |
264 | smallint listing = 0; |
265 | smallint overwrite = O_PROMPT; |
266 | #if ENABLE_DESKTOP |
267 | uint32_t cdf_offset; |
268 | #endif |
269 | unsigned long total_usize; |
270 | unsigned long total_size; |
271 | unsigned total_entries; |
272 | int dst_fd = -1; |
273 | char *src_fn = NULL; |
274 | char *dst_fn = NULL; |
275 | llist_t *zaccept = NULL; |
276 | llist_t *zreject = NULL; |
277 | char *base_dir = NULL; |
278 | int i, opt; |
279 | int opt_range = 0; |
280 | char key_buf[80]; |
281 | struct stat stat_buf; |
282 | |
283 | /* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP: |
284 | * |
285 | * # /usr/bin/unzip -qq -v decompress_unlzma.i.zip |
286 | * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i |
287 | * # /usr/bin/unzip -q -v decompress_unlzma.i.zip |
288 | * Length Method Size Ratio Date Time CRC-32 Name |
289 | * -------- ------ ------- ----- ---- ---- ------ ---- |
290 | * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i |
291 | * -------- ------- --- ------- |
292 | * 204372 35278 83% 1 file |
293 | * # /usr/bin/unzip -v decompress_unlzma.i.zip |
294 | * Archive: decompress_unlzma.i.zip |
295 | * Length Method Size Ratio Date Time CRC-32 Name |
296 | * -------- ------ ------- ----- ---- ---- ------ ---- |
297 | * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i |
298 | * -------- ------- --- ------- |
299 | * 204372 35278 83% 1 file |
300 | * # unzip -v decompress_unlzma.i.zip |
301 | * Archive: decompress_unlzma.i.zip |
302 | * Length Date Time Name |
303 | * -------- ---- ---- ---- |
304 | * 204372 09-06-09 14:23 decompress_unlzma.i |
305 | * -------- ------- |
306 | * 204372 1 files |
307 | * # /usr/bin/unzip -l -qq decompress_unlzma.i.zip |
308 | * 204372 09-06-09 14:23 decompress_unlzma.i |
309 | * # /usr/bin/unzip -l -q decompress_unlzma.i.zip |
310 | * Length Date Time Name |
311 | * -------- ---- ---- ---- |
312 | * 204372 09-06-09 14:23 decompress_unlzma.i |
313 | * -------- ------- |
314 | * 204372 1 file |
315 | * # /usr/bin/unzip -l decompress_unlzma.i.zip |
316 | * Archive: decompress_unlzma.i.zip |
317 | * Length Date Time Name |
318 | * -------- ---- ---- ---- |
319 | * 204372 09-06-09 14:23 decompress_unlzma.i |
320 | * -------- ------- |
321 | * 204372 1 file |
322 | */ |
323 | |
324 | /* '-' makes getopt return 1 for non-options */ |
325 | while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) { |
326 | switch (opt_range) { |
327 | case 0: /* Options */ |
328 | switch (opt) { |
329 | case 'l': /* List */ |
330 | listing = 1; |
331 | break; |
332 | |
333 | case 'n': /* Never overwrite existing files */ |
334 | overwrite = O_NEVER; |
335 | break; |
336 | |
337 | case 'o': /* Always overwrite existing files */ |
338 | overwrite = O_ALWAYS; |
339 | break; |
340 | |
341 | case 'p': /* Extract files to stdout and fall through to set verbosity */ |
342 | dst_fd = STDOUT_FILENO; |
343 | |
344 | case 'q': /* Be quiet */ |
345 | quiet++; |
346 | break; |
347 | |
348 | case 'v': /* Verbose list */ |
349 | IF_DESKTOP(verbose++;) |
350 | listing = 1; |
351 | break; |
352 | |
353 | case 1: /* The zip file */ |
354 | /* +5: space for ".zip" and NUL */ |
355 | src_fn = xmalloc(strlen(optarg) + 5); |
356 | strcpy(src_fn, optarg); |
357 | opt_range++; |
358 | break; |
359 | |
360 | default: |
361 | bb_show_usage(); |
362 | |
363 | } |
364 | break; |
365 | |
366 | case 1: /* Include files */ |
367 | if (opt == 1) { |
368 | llist_add_to(&zaccept, optarg); |
369 | break; |
370 | } |
371 | if (opt == 'd') { |
372 | base_dir = optarg; |
373 | opt_range += 2; |
374 | break; |
375 | } |
376 | if (opt == 'x') { |
377 | opt_range++; |
378 | break; |
379 | } |
380 | bb_show_usage(); |
381 | |
382 | case 2 : /* Exclude files */ |
383 | if (opt == 1) { |
384 | llist_add_to(&zreject, optarg); |
385 | break; |
386 | } |
387 | if (opt == 'd') { /* Extract to base directory */ |
388 | base_dir = optarg; |
389 | opt_range++; |
390 | break; |
391 | } |
392 | /* fall through */ |
393 | |
394 | default: |
395 | bb_show_usage(); |
396 | } |
397 | } |
398 | |
399 | if (src_fn == NULL) { |
400 | bb_show_usage(); |
401 | } |
402 | |
403 | /* Open input file */ |
404 | if (LONE_DASH(src_fn)) { |
405 | xdup2(STDIN_FILENO, zip_fd); |
406 | /* Cannot use prompt mode since zip data is arriving on STDIN */ |
407 | if (overwrite == O_PROMPT) |
408 | overwrite = O_NEVER; |
409 | } else { |
410 | static const char extn[][5] = {"", ".zip", ".ZIP"}; |
411 | int orig_src_fn_len = strlen(src_fn); |
412 | int src_fd = -1; |
413 | |
414 | for (i = 0; (i < 3) && (src_fd == -1); i++) { |
415 | strcpy(src_fn + orig_src_fn_len, extn[i]); |
416 | src_fd = open(src_fn, O_RDONLY); |
417 | } |
418 | if (src_fd == -1) { |
419 | src_fn[orig_src_fn_len] = '\0'; |
420 | bb_error_msg_and_die("can't open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn); |
421 | } |
422 | xmove_fd(src_fd, zip_fd); |
423 | } |
424 | |
425 | /* Change dir if necessary */ |
426 | if (base_dir) |
427 | xchdir(base_dir); |
428 | |
429 | if (quiet <= 1) { /* not -qq */ |
430 | if (quiet == 0) |
431 | printf("Archive: %s\n", src_fn); |
432 | if (listing) { |
433 | puts(verbose ? |
434 | " Length Method Size Ratio Date Time CRC-32 Name\n" |
435 | "-------- ------ ------- ----- ---- ---- ------ ----" |
436 | : |
437 | " Length Date Time Name\n" |
438 | " -------- ---- ---- ----" |
439 | ); |
440 | } |
441 | } |
442 | |
443 | /* Example of an archive with one 0-byte long file named 'z' |
444 | * created by Zip 2.31 on Unix: |
445 | * 0000 [50 4b]03 04 0a 00 00 00 00 00 42 1a b8 3c 00 00 |PK........B..<..| |
446 | * sig........ vneed flags compr mtime mdate crc32> |
447 | * 0010 00 00 00 00 00 00 00 00 00 00 01 00 15 00 7a 55 |..............zU| |
448 | * >..... csize...... usize...... fnlen exlen fn ex> |
449 | * 0020 54 09 00 03 cc d3 f9 4b cc d3 f9 4b 55 78 04 00 |T......K...KUx..| |
450 | * >tra_field...................................... |
451 | * 0030 00 00 00 00[50 4b]01 02 17 03 0a 00 00 00 00 00 |....PK..........| |
452 | * ........... sig........ vmade vneed flags compr |
453 | * 0040 42 1a b8 3c 00 00 00 00 00 00 00 00 00 00 00 00 |B..<............| |
454 | * mtime mdate crc32...... csize...... usize...... |
455 | * 0050 01 00 0d 00 00 00 00 00 00 00 00 00 a4 81 00 00 |................| |
456 | * fnlen exlen clen. dnum. iattr eattr...... relofs> (eattr = rw-r--r--) |
457 | * 0060 00 00 7a 55 54 05 00 03 cc d3 f9 4b 55 78 00 00 |..zUT......KUx..| |
458 | * >..... fn extra_field........................... |
459 | * 0070 [50 4b]05 06 00 00 00 00 01 00 01 00 3c 00 00 00 |PK..........<...| |
460 | * 0080 34 00 00 00 00 00 |4.....| |
461 | */ |
462 | total_usize = 0; |
463 | total_size = 0; |
464 | total_entries = 0; |
465 | #if ENABLE_DESKTOP |
466 | cdf_offset = 0; |
467 | #endif |
468 | while (1) { |
469 | uint32_t magic; |
470 | mode_t dir_mode = 0777; |
471 | #if ENABLE_DESKTOP |
472 | mode_t file_mode = 0666; |
473 | #endif |
474 | |
475 | /* Check magic number */ |
476 | xread(zip_fd, &magic, 4); |
477 | /* Central directory? It's at the end, so exit */ |
478 | if (magic == ZIP_CDF_MAGIC) |
479 | break; |
480 | #if ENABLE_DESKTOP |
481 | /* Data descriptor? It was a streaming file, go on */ |
482 | if (magic == ZIP_DD_MAGIC) { |
483 | /* skip over duplicate crc32, cmpsize and ucmpsize */ |
484 | unzip_skip(3 * 4); |
485 | continue; |
486 | } |
487 | #endif |
488 | if (magic != ZIP_FILEHEADER_MAGIC) |
489 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); |
490 | |
491 | /* Read the file header */ |
492 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); |
493 | FIX_ENDIANNESS_ZIP(zip_header); |
494 | if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { |
495 | bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); |
496 | } |
497 | #if !ENABLE_DESKTOP |
498 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { |
499 | bb_error_msg_and_die("zip flags 1 and 8 are not supported"); |
500 | } |
501 | #else |
502 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { |
503 | /* 0x0001 - encrypted */ |
504 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); |
505 | } |
506 | |
507 | { |
508 | cdf_header_t cdf_header; |
509 | cdf_offset = read_next_cdf(cdf_offset, &cdf_header); |
510 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { |
511 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten |
512 | * only from Central Directory. See unzip_doc.txt */ |
513 | zip_header.formatted.crc32 = cdf_header.formatted.crc32; |
514 | zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; |
515 | zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; |
516 | } |
517 | if ((cdf_header.formatted.version_made_by >> 8) == 3) { |
518 | /* this archive is created on Unix */ |
519 | dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); |
520 | } |
521 | } |
522 | #endif |
523 | |
524 | /* Read filename */ |
525 | free(dst_fn); |
526 | dst_fn = xzalloc(zip_header.formatted.filename_len + 1); |
527 | xread(zip_fd, dst_fn, zip_header.formatted.filename_len); |
528 | |
529 | /* Skip extra header bytes */ |
530 | unzip_skip(zip_header.formatted.extra_len); |
531 | |
532 | /* Filter zip entries */ |
533 | if (find_list_entry(zreject, dst_fn) |
534 | || (zaccept && !find_list_entry(zaccept, dst_fn)) |
535 | ) { /* Skip entry */ |
536 | i = 'n'; |
537 | |
538 | } else { /* Extract entry */ |
539 | if (listing) { /* List entry */ |
540 | unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); |
541 | if (!verbose) { |
542 | // " Length Date Time Name\n" |
543 | // " -------- ---- ---- ----" |
544 | printf( "%9u %02u-%02u-%02u %02u:%02u %s\n", |
545 | (unsigned)zip_header.formatted.ucmpsize, |
546 | (dostime & 0x01e00000) >> 21, |
547 | (dostime & 0x001f0000) >> 16, |
548 | (((dostime & 0xfe000000) >> 25) + 1980) % 100, |
549 | (dostime & 0x0000f800) >> 11, |
550 | (dostime & 0x000007e0) >> 5, |
551 | dst_fn); |
552 | total_usize += zip_header.formatted.ucmpsize; |
553 | } else { |
554 | unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize; |
555 | percents = percents * 100; |
556 | if (zip_header.formatted.ucmpsize) |
557 | percents /= zip_header.formatted.ucmpsize; |
558 | // " Length Method Size Ratio Date Time CRC-32 Name\n" |
559 | // "-------- ------ ------- ----- ---- ---- ------ ----" |
560 | printf( "%8u Defl:N" "%9u%4u%% %02u-%02u-%02u %02u:%02u %08x %s\n", |
561 | (unsigned)zip_header.formatted.ucmpsize, |
562 | (unsigned)zip_header.formatted.cmpsize, |
563 | (unsigned)percents, |
564 | (dostime & 0x01e00000) >> 21, |
565 | (dostime & 0x001f0000) >> 16, |
566 | (((dostime & 0xfe000000) >> 25) + 1980) % 100, |
567 | (dostime & 0x0000f800) >> 11, |
568 | (dostime & 0x000007e0) >> 5, |
569 | zip_header.formatted.crc32, |
570 | dst_fn); |
571 | total_usize += zip_header.formatted.ucmpsize; |
572 | total_size += zip_header.formatted.cmpsize; |
573 | } |
574 | i = 'n'; |
575 | } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ |
576 | i = -1; |
577 | } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ |
578 | if (stat(dst_fn, &stat_buf) == -1) { |
579 | if (errno != ENOENT) { |
580 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
581 | } |
582 | if (!quiet) { |
583 | printf(" creating: %s\n", dst_fn); |
584 | } |
585 | unzip_create_leading_dirs(dst_fn); |
586 | if (bb_make_directory(dst_fn, dir_mode, 0)) { |
587 | bb_error_msg_and_die("exiting"); |
588 | } |
589 | } else { |
590 | if (!S_ISDIR(stat_buf.st_mode)) { |
591 | bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); |
592 | } |
593 | } |
594 | i = 'n'; |
595 | |
596 | } else { /* Extract file */ |
597 | check_file: |
598 | if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ |
599 | if (errno != ENOENT) { |
600 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
601 | } |
602 | i = 'y'; |
603 | } else { /* File already exists */ |
604 | if (overwrite == O_NEVER) { |
605 | i = 'n'; |
606 | } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ |
607 | if (overwrite == O_ALWAYS) { |
608 | i = 'y'; |
609 | } else { |
610 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); |
611 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
612 | bb_perror_msg_and_die("can't read input"); |
613 | } |
614 | i = key_buf[0]; |
615 | } |
616 | } else { /* File is not regular file */ |
617 | bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); |
618 | } |
619 | } |
620 | } |
621 | } |
622 | |
623 | switch (i) { |
624 | case 'A': |
625 | overwrite = O_ALWAYS; |
626 | case 'y': /* Open file and fall into unzip */ |
627 | unzip_create_leading_dirs(dst_fn); |
628 | #if ENABLE_DESKTOP |
629 | dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); |
630 | #else |
631 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); |
632 | #endif |
633 | case -1: /* Unzip */ |
634 | if (!quiet) { |
635 | printf(" inflating: %s\n", dst_fn); |
636 | } |
637 | unzip_extract(&zip_header, dst_fd); |
638 | if (dst_fd != STDOUT_FILENO) { |
639 | /* closing STDOUT is potentially bad for future business */ |
640 | close(dst_fd); |
641 | } |
642 | break; |
643 | |
644 | case 'N': |
645 | overwrite = O_NEVER; |
646 | case 'n': |
647 | /* Skip entry data */ |
648 | unzip_skip(zip_header.formatted.cmpsize); |
649 | break; |
650 | |
651 | case 'r': |
652 | /* Prompt for new name */ |
653 | printf("new name: "); |
654 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
655 | bb_perror_msg_and_die("can't read input"); |
656 | } |
657 | free(dst_fn); |
658 | dst_fn = xstrdup(key_buf); |
659 | chomp(dst_fn); |
660 | goto check_file; |
661 | |
662 | default: |
663 | printf("error: invalid response [%c]\n", (char)i); |
664 | goto check_file; |
665 | } |
666 | |
667 | total_entries++; |
668 | } |
669 | |
670 | if (listing && quiet <= 1) { |
671 | if (!verbose) { |
672 | // " Length Date Time Name\n" |
673 | // " -------- ---- ---- ----" |
674 | printf( " -------- -------\n" |
675 | "%9lu" " %u files\n", |
676 | total_usize, total_entries); |
677 | } else { |
678 | unsigned long percents = total_usize - total_size; |
679 | percents = percents * 100; |
680 | if (total_usize) |
681 | percents /= total_usize; |
682 | // " Length Method Size Ratio Date Time CRC-32 Name\n" |
683 | // "-------- ------ ------- ----- ---- ---- ------ ----" |
684 | printf( "-------- ------- --- -------\n" |
685 | "%8lu" "%17lu%4u%% %u files\n", |
686 | total_usize, total_size, (unsigned)percents, |
687 | total_entries); |
688 | } |
689 | } |
690 | |
691 | return 0; |
692 | } |