Annotation of /trunk/mkinitrd-magellan/busybox/archival/unzip.c
Parent Directory | Revision Log
Revision 1123 -
(hide annotations)
(download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 21566 byte(s)
Wed Aug 18 21:56:57 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 21566 byte(s)
-updated to busybox-1.17.1
1 | niro | 532 | /* 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 | niro | 816 | #include "libbb.h" |
23 | niro | 532 | #include "unarchive.h" |
24 | |||
25 | niro | 816 | enum { |
26 | #if BB_BIG_ENDIAN | ||
27 | ZIP_FILEHEADER_MAGIC = 0x504b0304, | ||
28 | niro | 1123 | ZIP_CDF_MAGIC = 0x504b0102, /* central directory's file header */ |
29 | ZIP_CDE_MAGIC = 0x504b0506, /* "end of central directory" record */ | ||
30 | niro | 816 | ZIP_DD_MAGIC = 0x504b0708, |
31 | #else | ||
32 | ZIP_FILEHEADER_MAGIC = 0x04034b50, | ||
33 | niro | 1123 | ZIP_CDF_MAGIC = 0x02014b50, |
34 | niro | 816 | ZIP_CDE_MAGIC = 0x06054b50, |
35 | ZIP_DD_MAGIC = 0x08074b50, | ||
36 | #endif | ||
37 | }; | ||
38 | niro | 532 | |
39 | niro | 816 | #define ZIP_HEADER_LEN 26 |
40 | |||
41 | niro | 532 | typedef union { |
42 | niro | 816 | uint8_t raw[ZIP_HEADER_LEN]; |
43 | niro | 532 | struct { |
44 | niro | 816 | uint16_t version; /* 0-1 */ |
45 | niro | 1123 | uint16_t zip_flags; /* 2-3 */ |
46 | niro | 816 | 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 | niro | 532 | |
57 | niro | 816 | /* 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 | niro | 1123 | #define CDF_HEADER_LEN 42 |
80 | niro | 816 | |
81 | typedef union { | ||
82 | niro | 1123 | uint8_t raw[CDF_HEADER_LEN]; |
83 | niro | 816 | 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 | niro | 1123 | uint16_t cdf_flags; /* 4-5 */ |
88 | niro | 816 | 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 | niro | 1123 | } cdf_header_t; |
103 | niro | 816 | |
104 | niro | 1123 | 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 | niro | 816 | }; |
109 | |||
110 | niro | 1123 | #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 | niro | 816 | } 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 | niro | 1123 | 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 | niro | 816 | /* 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 | niro | 1123 | (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ |
147 | niro | 816 | } while (0) |
148 | |||
149 | enum { zip_fd = 3 }; | ||
150 | |||
151 | |||
152 | #if ENABLE_DESKTOP | ||
153 | niro | 1123 | |
154 | #define PEEK_FROM_END 16384 | ||
155 | |||
156 | niro | 816 | /* NB: does not preserve file position! */ |
157 | niro | 1123 | static uint32_t find_cdf_offset(void) |
158 | niro | 532 | { |
159 | niro | 816 | cde_header_t cde_header; |
160 | unsigned char *p; | ||
161 | off_t end; | ||
162 | niro | 1123 | unsigned char *buf = xzalloc(PEEK_FROM_END); |
163 | niro | 816 | |
164 | end = xlseek(zip_fd, 0, SEEK_END); | ||
165 | niro | 1123 | end -= PEEK_FROM_END; |
166 | if (end < 0) | ||
167 | end = 0; | ||
168 | niro | 816 | xlseek(zip_fd, end, SEEK_SET); |
169 | niro | 1123 | full_read(zip_fd, buf, PEEK_FROM_END); |
170 | niro | 816 | |
171 | p = buf; | ||
172 | niro | 1123 | while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { |
173 | niro | 816 | 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 | niro | 1123 | free(buf); |
187 | return cde_header.formatted.cdf_offset; | ||
188 | niro | 532 | } |
189 | niro | 1123 | //free(buf); |
190 | niro | 816 | bb_error_msg_and_die("can't find file table"); |
191 | }; | ||
192 | |||
193 | niro | 1123 | static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) |
194 | niro | 816 | { |
195 | off_t org; | ||
196 | |||
197 | org = xlseek(zip_fd, 0, SEEK_CUR); | ||
198 | |||
199 | niro | 1123 | if (!cdf_offset) |
200 | cdf_offset = find_cdf_offset(); | ||
201 | niro | 816 | |
202 | niro | 1123 | 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 | niro | 816 | |
210 | xlseek(zip_fd, org, SEEK_SET); | ||
211 | niro | 1123 | return cdf_offset; |
212 | niro | 816 | }; |
213 | #endif | ||
214 | |||
215 | static void unzip_skip(off_t skip) | ||
216 | { | ||
217 | niro | 984 | if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) |
218 | bb_copyfd_exact_size(zip_fd, -1, skip); | ||
219 | niro | 532 | } |
220 | |||
221 | niro | 816 | static void unzip_create_leading_dirs(const char *fn) |
222 | niro | 532 | { |
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 | niro | 816 | static void unzip_extract(zip_header_t *zip_header, int dst_fd) |
232 | niro | 532 | { |
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 | niro | 816 | bb_copyfd_exact_size(zip_fd, dst_fd, size); |
238 | niro | 532 | } else { |
239 | /* Method 8 - inflate */ | ||
240 | inflate_unzip_result res; | ||
241 | niro | 816 | if (inflate_unzip(&res, zip_header->formatted.cmpsize, zip_fd, dst_fd) < 0) |
242 | bb_error_msg_and_die("inflate error"); | ||
243 | niro | 532 | /* Validate decompression - crc */ |
244 | if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) { | ||
245 | niro | 816 | bb_error_msg_and_die("crc error"); |
246 | niro | 532 | } |
247 | /* Validate decompression - size */ | ||
248 | if (zip_header->formatted.ucmpsize != res.bytes_out) { | ||
249 | niro | 816 | /* Don't die. Who knows, maybe len calculation |
250 | * was botched somewhere. After all, crc matched! */ | ||
251 | bb_error_msg("bad length"); | ||
252 | niro | 532 | } |
253 | } | ||
254 | } | ||
255 | |||
256 | niro | 816 | int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
257 | niro | 532 | int unzip_main(int argc, char **argv) |
258 | { | ||
259 | niro | 816 | enum { O_PROMPT, O_NEVER, O_ALWAYS }; |
260 | |||
261 | niro | 532 | zip_header_t zip_header; |
262 | niro | 984 | smallint quiet = 0; |
263 | IF_NOT_DESKTOP(const) smallint verbose = 0; | ||
264 | niro | 816 | smallint listing = 0; |
265 | smallint overwrite = O_PROMPT; | ||
266 | #if ENABLE_DESKTOP | ||
267 | niro | 1123 | uint32_t cdf_offset; |
268 | niro | 816 | #endif |
269 | niro | 984 | unsigned long total_usize; |
270 | unsigned long total_size; | ||
271 | niro | 816 | unsigned total_entries; |
272 | int dst_fd = -1; | ||
273 | char *src_fn = NULL; | ||
274 | char *dst_fn = NULL; | ||
275 | niro | 532 | llist_t *zaccept = NULL; |
276 | llist_t *zreject = NULL; | ||
277 | char *base_dir = NULL; | ||
278 | niro | 816 | int i, opt; |
279 | int opt_range = 0; | ||
280 | char key_buf[80]; | ||
281 | niro | 532 | struct stat stat_buf; |
282 | |||
283 | niro | 984 | /* -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 | niro | 816 | /* '-' makes getopt return 1 for non-options */ |
325 | niro | 984 | while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) { |
326 | niro | 532 | switch (opt_range) { |
327 | case 0: /* Options */ | ||
328 | switch (opt) { | ||
329 | case 'l': /* List */ | ||
330 | niro | 816 | listing = 1; |
331 | niro | 532 | break; |
332 | |||
333 | case 'n': /* Never overwrite existing files */ | ||
334 | niro | 816 | overwrite = O_NEVER; |
335 | niro | 532 | break; |
336 | |||
337 | case 'o': /* Always overwrite existing files */ | ||
338 | niro | 816 | overwrite = O_ALWAYS; |
339 | niro | 532 | 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 | niro | 984 | quiet++; |
346 | niro | 532 | break; |
347 | |||
348 | niro | 984 | case 'v': /* Verbose list */ |
349 | IF_DESKTOP(verbose++;) | ||
350 | listing = 1; | ||
351 | break; | ||
352 | |||
353 | niro | 816 | 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 | niro | 532 | 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 | niro | 816 | break; |
370 | } | ||
371 | if (opt == 'd') { | ||
372 | niro | 532 | base_dir = optarg; |
373 | opt_range += 2; | ||
374 | niro | 816 | break; |
375 | } | ||
376 | if (opt == 'x') { | ||
377 | niro | 532 | opt_range++; |
378 | niro | 816 | break; |
379 | niro | 532 | } |
380 | niro | 816 | bb_show_usage(); |
381 | niro | 532 | |
382 | case 2 : /* Exclude files */ | ||
383 | if (opt == 1) { | ||
384 | llist_add_to(&zreject, optarg); | ||
385 | niro | 816 | break; |
386 | } | ||
387 | if (opt == 'd') { /* Extract to base directory */ | ||
388 | niro | 532 | base_dir = optarg; |
389 | opt_range++; | ||
390 | niro | 816 | break; |
391 | niro | 532 | } |
392 | niro | 816 | /* fall through */ |
393 | niro | 532 | |
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 | niro | 816 | xdup2(STDIN_FILENO, zip_fd); |
406 | niro | 532 | /* Cannot use prompt mode since zip data is arriving on STDIN */ |
407 | niro | 816 | if (overwrite == O_PROMPT) |
408 | overwrite = O_NEVER; | ||
409 | niro | 532 | } else { |
410 | niro | 816 | static const char extn[][5] = {"", ".zip", ".ZIP"}; |
411 | niro | 532 | int orig_src_fn_len = strlen(src_fn); |
412 | niro | 816 | int src_fd = -1; |
413 | |||
414 | niro | 532 | 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 | niro | 816 | 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 | niro | 532 | } |
422 | niro | 816 | xmove_fd(src_fd, zip_fd); |
423 | niro | 532 | } |
424 | |||
425 | /* Change dir if necessary */ | ||
426 | if (base_dir) | ||
427 | xchdir(base_dir); | ||
428 | |||
429 | niro | 984 | 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 | niro | 816 | } |
441 | } | ||
442 | niro | 532 | |
443 | niro | 1123 | /* 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 | niro | 984 | total_usize = 0; |
463 | niro | 816 | total_size = 0; |
464 | total_entries = 0; | ||
465 | #if ENABLE_DESKTOP | ||
466 | niro | 1123 | cdf_offset = 0; |
467 | niro | 816 | #endif |
468 | niro | 532 | while (1) { |
469 | niro | 816 | uint32_t magic; |
470 | niro | 1123 | mode_t dir_mode = 0777; |
471 | #if ENABLE_DESKTOP | ||
472 | mode_t file_mode = 0666; | ||
473 | #endif | ||
474 | niro | 532 | |
475 | /* Check magic number */ | ||
476 | niro | 816 | xread(zip_fd, &magic, 4); |
477 | /* Central directory? It's at the end, so exit */ | ||
478 | niro | 1123 | if (magic == ZIP_CDF_MAGIC) |
479 | niro | 532 | break; |
480 | niro | 816 | #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 | niro | 532 | } |
487 | niro | 816 | #endif |
488 | if (magic != ZIP_FILEHEADER_MAGIC) | ||
489 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); | ||
490 | niro | 532 | |
491 | /* Read the file header */ | ||
492 | niro | 816 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); |
493 | FIX_ENDIANNESS_ZIP(zip_header); | ||
494 | niro | 532 | if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { |
495 | niro | 816 | bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); |
496 | niro | 532 | } |
497 | niro | 816 | #if !ENABLE_DESKTOP |
498 | niro | 1123 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { |
499 | niro | 816 | bb_error_msg_and_die("zip flags 1 and 8 are not supported"); |
500 | } | ||
501 | #else | ||
502 | niro | 1123 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { |
503 | niro | 816 | /* 0x0001 - encrypted */ |
504 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); | ||
505 | } | ||
506 | niro | 1123 | |
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 | niro | 816 | } |
522 | #endif | ||
523 | niro | 532 | |
524 | /* Read filename */ | ||
525 | free(dst_fn); | ||
526 | dst_fn = xzalloc(zip_header.formatted.filename_len + 1); | ||
527 | niro | 816 | xread(zip_fd, dst_fn, zip_header.formatted.filename_len); |
528 | niro | 532 | |
529 | /* Skip extra header bytes */ | ||
530 | niro | 816 | unzip_skip(zip_header.formatted.extra_len); |
531 | niro | 532 | |
532 | /* Filter zip entries */ | ||
533 | niro | 816 | if (find_list_entry(zreject, dst_fn) |
534 | || (zaccept && !find_list_entry(zaccept, dst_fn)) | ||
535 | ) { /* Skip entry */ | ||
536 | niro | 532 | i = 'n'; |
537 | |||
538 | } else { /* Extract entry */ | ||
539 | niro | 816 | if (listing) { /* List entry */ |
540 | niro | 984 | 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 | niro | 816 | } else { |
554 | niro | 984 | 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 | niro | 816 | } |
574 | niro | 532 | 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 | niro | 816 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
581 | niro | 532 | } |
582 | niro | 984 | if (!quiet) { |
583 | niro | 532 | printf(" creating: %s\n", dst_fn); |
584 | } | ||
585 | unzip_create_leading_dirs(dst_fn); | ||
586 | niro | 1123 | if (bb_make_directory(dst_fn, dir_mode, 0)) { |
587 | niro | 532 | 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 | niro | 816 | check_file: |
598 | niro | 532 | if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ |
599 | if (errno != ENOENT) { | ||
600 | niro | 816 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
601 | niro | 532 | } |
602 | i = 'y'; | ||
603 | } else { /* File already exists */ | ||
604 | niro | 816 | if (overwrite == O_NEVER) { |
605 | niro | 532 | i = 'n'; |
606 | } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ | ||
607 | niro | 816 | if (overwrite == O_ALWAYS) { |
608 | niro | 532 | i = 'y'; |
609 | } else { | ||
610 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); | ||
611 | niro | 816 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
612 | bb_perror_msg_and_die("can't read input"); | ||
613 | niro | 532 | } |
614 | i = key_buf[0]; | ||
615 | } | ||
616 | } else { /* File is not regular file */ | ||
617 | niro | 816 | bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); |
618 | niro | 532 | } |
619 | } | ||
620 | } | ||
621 | } | ||
622 | |||
623 | switch (i) { | ||
624 | case 'A': | ||
625 | niro | 816 | overwrite = O_ALWAYS; |
626 | niro | 532 | case 'y': /* Open file and fall into unzip */ |
627 | unzip_create_leading_dirs(dst_fn); | ||
628 | niro | 1123 | #if ENABLE_DESKTOP |
629 | dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); | ||
630 | #else | ||
631 | niro | 532 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); |
632 | niro | 1123 | #endif |
633 | niro | 532 | case -1: /* Unzip */ |
634 | niro | 984 | if (!quiet) { |
635 | niro | 532 | printf(" inflating: %s\n", dst_fn); |
636 | } | ||
637 | niro | 816 | unzip_extract(&zip_header, dst_fd); |
638 | niro | 532 | 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 | niro | 816 | overwrite = O_NEVER; |
646 | niro | 532 | case 'n': |
647 | /* Skip entry data */ | ||
648 | niro | 816 | unzip_skip(zip_header.formatted.cmpsize); |
649 | niro | 532 | break; |
650 | |||
651 | case 'r': | ||
652 | /* Prompt for new name */ | ||
653 | printf("new name: "); | ||
654 | niro | 816 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
655 | bb_perror_msg_and_die("can't read input"); | ||
656 | niro | 532 | } |
657 | free(dst_fn); | ||
658 | dst_fn = xstrdup(key_buf); | ||
659 | chomp(dst_fn); | ||
660 | niro | 816 | goto check_file; |
661 | niro | 532 | |
662 | default: | ||
663 | niro | 984 | printf("error: invalid response [%c]\n", (char)i); |
664 | niro | 816 | goto check_file; |
665 | niro | 532 | } |
666 | |||
667 | niro | 816 | total_entries++; |
668 | niro | 532 | } |
669 | |||
670 | niro | 984 | 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 | niro | 532 | } |
690 | |||
691 | niro | 816 | return 0; |
692 | niro | 532 | } |