Annotation of /tags/mkinitrd-6_1_8/busybox/archival/unzip.c
Parent Directory | Revision Log
Revision 916 -
(hide annotations)
(download)
Wed Oct 28 00:17:50 2009 UTC (14 years, 8 months ago) by niro
File MIME type: text/plain
File size: 15804 byte(s)
Wed Oct 28 00:17:50 2009 UTC (14 years, 8 months ago) by niro
File MIME type: text/plain
File size: 15804 byte(s)
tagged 'mkinitrd-6_1_8'
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 | ZIP_CDS_MAGIC = 0x504b0102, | ||
29 | ZIP_CDE_MAGIC = 0x504b0506, | ||
30 | ZIP_DD_MAGIC = 0x504b0708, | ||
31 | #else | ||
32 | ZIP_FILEHEADER_MAGIC = 0x04034b50, | ||
33 | ZIP_CDS_MAGIC = 0x02014b50, | ||
34 | 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 | uint16_t 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 | 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.flags = SWAP_LE16((zip_header).formatted.flags ); \ | ||
70 | (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ | ||
71 | (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ | ||
72 | (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ | ||
73 | (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ | ||
74 | (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ | ||
75 | (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ | ||
76 | (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ | ||
77 | (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ | ||
78 | } while (0) | ||
79 | |||
80 | #define CDS_HEADER_LEN 42 | ||
81 | |||
82 | typedef union { | ||
83 | uint8_t raw[CDS_HEADER_LEN]; | ||
84 | struct { | ||
85 | /* uint32_t signature; 50 4b 01 02 */ | ||
86 | uint16_t version_made_by; /* 0-1 */ | ||
87 | uint16_t version_needed; /* 2-3 */ | ||
88 | uint16_t cds_flags; /* 4-5 */ | ||
89 | uint16_t method; /* 6-7 */ | ||
90 | uint16_t mtime; /* 8-9 */ | ||
91 | uint16_t mdate; /* 10-11 */ | ||
92 | uint32_t crc32; /* 12-15 */ | ||
93 | uint32_t cmpsize; /* 16-19 */ | ||
94 | uint32_t ucmpsize; /* 20-23 */ | ||
95 | uint16_t file_name_length; /* 24-25 */ | ||
96 | uint16_t extra_field_length; /* 26-27 */ | ||
97 | uint16_t file_comment_length; /* 28-29 */ | ||
98 | uint16_t disk_number_start; /* 30-31 */ | ||
99 | uint16_t internal_file_attributes; /* 32-33 */ | ||
100 | uint32_t external_file_attributes PACKED; /* 34-37 */ | ||
101 | uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ | ||
102 | } formatted PACKED; | ||
103 | } cds_header_t; | ||
104 | |||
105 | struct BUG_cds_header_must_be_42_bytes { | ||
106 | char BUG_cds_header_must_be_42_bytes[ | ||
107 | offsetof(cds_header_t, formatted.relative_offset_of_local_header) + 4 | ||
108 | == CDS_HEADER_LEN ? 1 : -1]; | ||
109 | }; | ||
110 | |||
111 | #define FIX_ENDIANNESS_CDS(cds_header) do { \ | ||
112 | (cds_header).formatted.crc32 = SWAP_LE32((cds_header).formatted.crc32 ); \ | ||
113 | (cds_header).formatted.cmpsize = SWAP_LE32((cds_header).formatted.cmpsize ); \ | ||
114 | (cds_header).formatted.ucmpsize = SWAP_LE32((cds_header).formatted.ucmpsize ); \ | ||
115 | (cds_header).formatted.file_name_length = SWAP_LE16((cds_header).formatted.file_name_length); \ | ||
116 | (cds_header).formatted.extra_field_length = SWAP_LE16((cds_header).formatted.extra_field_length); \ | ||
117 | (cds_header).formatted.file_comment_length = SWAP_LE16((cds_header).formatted.file_comment_length); \ | ||
118 | } while (0) | ||
119 | |||
120 | #define CDE_HEADER_LEN 16 | ||
121 | |||
122 | typedef union { | ||
123 | uint8_t raw[CDE_HEADER_LEN]; | ||
124 | struct { | ||
125 | /* uint32_t signature; 50 4b 05 06 */ | ||
126 | uint16_t this_disk_no; | ||
127 | uint16_t disk_with_cds_no; | ||
128 | uint16_t cds_entries_on_this_disk; | ||
129 | uint16_t cds_entries_total; | ||
130 | uint32_t cds_size; | ||
131 | uint32_t cds_offset; | ||
132 | /* uint16_t file_comment_length; */ | ||
133 | /* .ZIP file comment (variable size) */ | ||
134 | } formatted PACKED; | ||
135 | } cde_header_t; | ||
136 | |||
137 | struct BUG_cde_header_must_be_16_bytes { | ||
138 | char BUG_cde_header_must_be_16_bytes[ | ||
139 | sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; | ||
140 | }; | ||
141 | |||
142 | #define FIX_ENDIANNESS_CDE(cde_header) do { \ | ||
143 | (cde_header).formatted.cds_offset = SWAP_LE32((cde_header).formatted.cds_offset); \ | ||
144 | } while (0) | ||
145 | |||
146 | enum { zip_fd = 3 }; | ||
147 | |||
148 | |||
149 | #if ENABLE_DESKTOP | ||
150 | /* NB: does not preserve file position! */ | ||
151 | static uint32_t find_cds_offset(void) | ||
152 | niro | 532 | { |
153 | niro | 816 | unsigned char buf[1024]; |
154 | cde_header_t cde_header; | ||
155 | unsigned char *p; | ||
156 | off_t end; | ||
157 | |||
158 | end = xlseek(zip_fd, 0, SEEK_END); | ||
159 | if (end < 1024) | ||
160 | end = 1024; | ||
161 | end -= 1024; | ||
162 | xlseek(zip_fd, end, SEEK_SET); | ||
163 | full_read(zip_fd, buf, 1024); | ||
164 | |||
165 | p = buf; | ||
166 | while (p <= buf + 1024 - CDE_HEADER_LEN - 4) { | ||
167 | if (*p != 'P') { | ||
168 | p++; | ||
169 | continue; | ||
170 | } | ||
171 | if (*++p != 'K') | ||
172 | continue; | ||
173 | if (*++p != 5) | ||
174 | continue; | ||
175 | if (*++p != 6) | ||
176 | continue; | ||
177 | /* we found CDE! */ | ||
178 | memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); | ||
179 | FIX_ENDIANNESS_CDE(cde_header); | ||
180 | return cde_header.formatted.cds_offset; | ||
181 | niro | 532 | } |
182 | niro | 816 | bb_error_msg_and_die("can't find file table"); |
183 | }; | ||
184 | |||
185 | static uint32_t read_next_cds(int count_m1, uint32_t cds_offset, cds_header_t *cds_ptr) | ||
186 | { | ||
187 | off_t org; | ||
188 | |||
189 | org = xlseek(zip_fd, 0, SEEK_CUR); | ||
190 | |||
191 | if (!cds_offset) | ||
192 | cds_offset = find_cds_offset(); | ||
193 | |||
194 | while (count_m1-- >= 0) { | ||
195 | xlseek(zip_fd, cds_offset + 4, SEEK_SET); | ||
196 | xread(zip_fd, cds_ptr->raw, CDS_HEADER_LEN); | ||
197 | FIX_ENDIANNESS_CDS(*cds_ptr); | ||
198 | cds_offset += 4 + CDS_HEADER_LEN | ||
199 | + cds_ptr->formatted.file_name_length | ||
200 | + cds_ptr->formatted.extra_field_length | ||
201 | + cds_ptr->formatted.file_comment_length; | ||
202 | } | ||
203 | |||
204 | xlseek(zip_fd, org, SEEK_SET); | ||
205 | return cds_offset; | ||
206 | }; | ||
207 | #endif | ||
208 | |||
209 | static void unzip_skip(off_t skip) | ||
210 | { | ||
211 | bb_copyfd_exact_size(zip_fd, -1, skip); | ||
212 | niro | 532 | } |
213 | |||
214 | niro | 816 | static void unzip_create_leading_dirs(const char *fn) |
215 | niro | 532 | { |
216 | /* Create all leading directories */ | ||
217 | char *name = xstrdup(fn); | ||
218 | if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) { | ||
219 | bb_error_msg_and_die("exiting"); /* bb_make_directory is noisy */ | ||
220 | } | ||
221 | free(name); | ||
222 | } | ||
223 | |||
224 | niro | 816 | static void unzip_extract(zip_header_t *zip_header, int dst_fd) |
225 | niro | 532 | { |
226 | if (zip_header->formatted.method == 0) { | ||
227 | /* Method 0 - stored (not compressed) */ | ||
228 | off_t size = zip_header->formatted.ucmpsize; | ||
229 | if (size) | ||
230 | niro | 816 | bb_copyfd_exact_size(zip_fd, dst_fd, size); |
231 | niro | 532 | } else { |
232 | /* Method 8 - inflate */ | ||
233 | inflate_unzip_result res; | ||
234 | niro | 816 | if (inflate_unzip(&res, zip_header->formatted.cmpsize, zip_fd, dst_fd) < 0) |
235 | bb_error_msg_and_die("inflate error"); | ||
236 | niro | 532 | /* Validate decompression - crc */ |
237 | if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) { | ||
238 | niro | 816 | bb_error_msg_and_die("crc error"); |
239 | niro | 532 | } |
240 | /* Validate decompression - size */ | ||
241 | if (zip_header->formatted.ucmpsize != res.bytes_out) { | ||
242 | niro | 816 | /* Don't die. Who knows, maybe len calculation |
243 | * was botched somewhere. After all, crc matched! */ | ||
244 | bb_error_msg("bad length"); | ||
245 | niro | 532 | } |
246 | } | ||
247 | } | ||
248 | |||
249 | niro | 816 | int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
250 | niro | 532 | int unzip_main(int argc, char **argv) |
251 | { | ||
252 | niro | 816 | enum { O_PROMPT, O_NEVER, O_ALWAYS }; |
253 | |||
254 | niro | 532 | zip_header_t zip_header; |
255 | niro | 816 | smallint verbose = 1; |
256 | smallint listing = 0; | ||
257 | smallint overwrite = O_PROMPT; | ||
258 | #if ENABLE_DESKTOP | ||
259 | uint32_t cds_offset; | ||
260 | unsigned cds_entries; | ||
261 | #endif | ||
262 | unsigned total_size; | ||
263 | unsigned total_entries; | ||
264 | int dst_fd = -1; | ||
265 | char *src_fn = NULL; | ||
266 | char *dst_fn = NULL; | ||
267 | niro | 532 | llist_t *zaccept = NULL; |
268 | llist_t *zreject = NULL; | ||
269 | char *base_dir = NULL; | ||
270 | niro | 816 | int i, opt; |
271 | int opt_range = 0; | ||
272 | char key_buf[80]; | ||
273 | niro | 532 | struct stat stat_buf; |
274 | |||
275 | niro | 816 | /* '-' makes getopt return 1 for non-options */ |
276 | niro | 532 | while ((opt = getopt(argc, argv, "-d:lnopqx")) != -1) { |
277 | switch (opt_range) { | ||
278 | case 0: /* Options */ | ||
279 | switch (opt) { | ||
280 | case 'l': /* List */ | ||
281 | niro | 816 | listing = 1; |
282 | niro | 532 | break; |
283 | |||
284 | case 'n': /* Never overwrite existing files */ | ||
285 | niro | 816 | overwrite = O_NEVER; |
286 | niro | 532 | break; |
287 | |||
288 | case 'o': /* Always overwrite existing files */ | ||
289 | niro | 816 | overwrite = O_ALWAYS; |
290 | niro | 532 | break; |
291 | |||
292 | case 'p': /* Extract files to stdout and fall through to set verbosity */ | ||
293 | dst_fd = STDOUT_FILENO; | ||
294 | |||
295 | case 'q': /* Be quiet */ | ||
296 | niro | 816 | verbose = 0; |
297 | niro | 532 | break; |
298 | |||
299 | niro | 816 | case 1: /* The zip file */ |
300 | /* +5: space for ".zip" and NUL */ | ||
301 | src_fn = xmalloc(strlen(optarg) + 5); | ||
302 | strcpy(src_fn, optarg); | ||
303 | niro | 532 | opt_range++; |
304 | break; | ||
305 | |||
306 | default: | ||
307 | bb_show_usage(); | ||
308 | |||
309 | } | ||
310 | break; | ||
311 | |||
312 | case 1: /* Include files */ | ||
313 | if (opt == 1) { | ||
314 | llist_add_to(&zaccept, optarg); | ||
315 | niro | 816 | break; |
316 | } | ||
317 | if (opt == 'd') { | ||
318 | niro | 532 | base_dir = optarg; |
319 | opt_range += 2; | ||
320 | niro | 816 | break; |
321 | } | ||
322 | if (opt == 'x') { | ||
323 | niro | 532 | opt_range++; |
324 | niro | 816 | break; |
325 | niro | 532 | } |
326 | niro | 816 | bb_show_usage(); |
327 | niro | 532 | |
328 | case 2 : /* Exclude files */ | ||
329 | if (opt == 1) { | ||
330 | llist_add_to(&zreject, optarg); | ||
331 | niro | 816 | break; |
332 | } | ||
333 | if (opt == 'd') { /* Extract to base directory */ | ||
334 | niro | 532 | base_dir = optarg; |
335 | opt_range++; | ||
336 | niro | 816 | break; |
337 | niro | 532 | } |
338 | niro | 816 | /* fall through */ |
339 | niro | 532 | |
340 | default: | ||
341 | bb_show_usage(); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | if (src_fn == NULL) { | ||
346 | bb_show_usage(); | ||
347 | } | ||
348 | |||
349 | /* Open input file */ | ||
350 | if (LONE_DASH(src_fn)) { | ||
351 | niro | 816 | xdup2(STDIN_FILENO, zip_fd); |
352 | niro | 532 | /* Cannot use prompt mode since zip data is arriving on STDIN */ |
353 | niro | 816 | if (overwrite == O_PROMPT) |
354 | overwrite = O_NEVER; | ||
355 | niro | 532 | } else { |
356 | niro | 816 | static const char extn[][5] = {"", ".zip", ".ZIP"}; |
357 | niro | 532 | int orig_src_fn_len = strlen(src_fn); |
358 | niro | 816 | int src_fd = -1; |
359 | |||
360 | niro | 532 | for (i = 0; (i < 3) && (src_fd == -1); i++) { |
361 | strcpy(src_fn + orig_src_fn_len, extn[i]); | ||
362 | src_fd = open(src_fn, O_RDONLY); | ||
363 | } | ||
364 | if (src_fd == -1) { | ||
365 | niro | 816 | src_fn[orig_src_fn_len] = '\0'; |
366 | bb_error_msg_and_die("can't open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn); | ||
367 | niro | 532 | } |
368 | niro | 816 | xmove_fd(src_fd, zip_fd); |
369 | niro | 532 | } |
370 | |||
371 | /* Change dir if necessary */ | ||
372 | if (base_dir) | ||
373 | xchdir(base_dir); | ||
374 | |||
375 | niro | 816 | if (verbose) { |
376 | niro | 532 | printf("Archive: %s\n", src_fn); |
377 | niro | 816 | if (listing){ |
378 | puts(" Length Date Time Name\n" | ||
379 | " -------- ---- ---- ----"); | ||
380 | } | ||
381 | } | ||
382 | niro | 532 | |
383 | niro | 816 | total_size = 0; |
384 | total_entries = 0; | ||
385 | #if ENABLE_DESKTOP | ||
386 | cds_entries = 0; | ||
387 | cds_offset = 0; | ||
388 | #endif | ||
389 | niro | 532 | while (1) { |
390 | niro | 816 | uint32_t magic; |
391 | niro | 532 | |
392 | /* Check magic number */ | ||
393 | niro | 816 | xread(zip_fd, &magic, 4); |
394 | /* Central directory? It's at the end, so exit */ | ||
395 | if (magic == ZIP_CDS_MAGIC) | ||
396 | niro | 532 | break; |
397 | niro | 816 | #if ENABLE_DESKTOP |
398 | /* Data descriptor? It was a streaming file, go on */ | ||
399 | if (magic == ZIP_DD_MAGIC) { | ||
400 | /* skip over duplicate crc32, cmpsize and ucmpsize */ | ||
401 | unzip_skip(3 * 4); | ||
402 | continue; | ||
403 | niro | 532 | } |
404 | niro | 816 | #endif |
405 | if (magic != ZIP_FILEHEADER_MAGIC) | ||
406 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); | ||
407 | niro | 532 | |
408 | /* Read the file header */ | ||
409 | niro | 816 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); |
410 | FIX_ENDIANNESS_ZIP(zip_header); | ||
411 | niro | 532 | if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { |
412 | niro | 816 | bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); |
413 | niro | 532 | } |
414 | niro | 816 | #if !ENABLE_DESKTOP |
415 | if (zip_header.formatted.flags & 0x0009) { | ||
416 | bb_error_msg_and_die("zip flags 1 and 8 are not supported"); | ||
417 | } | ||
418 | #else | ||
419 | if (zip_header.formatted.flags & 0x0001) { | ||
420 | /* 0x0001 - encrypted */ | ||
421 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); | ||
422 | } | ||
423 | if (zip_header.formatted.flags & 0x0008) { | ||
424 | cds_header_t cds_header; | ||
425 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten | ||
426 | * only from Central Directory. See unzip_doc.txt */ | ||
427 | cds_offset = read_next_cds(total_entries - cds_entries, cds_offset, &cds_header); | ||
428 | cds_entries = total_entries + 1; | ||
429 | zip_header.formatted.crc32 = cds_header.formatted.crc32; | ||
430 | zip_header.formatted.cmpsize = cds_header.formatted.cmpsize; | ||
431 | zip_header.formatted.ucmpsize = cds_header.formatted.ucmpsize; | ||
432 | } | ||
433 | #endif | ||
434 | niro | 532 | |
435 | /* Read filename */ | ||
436 | free(dst_fn); | ||
437 | dst_fn = xzalloc(zip_header.formatted.filename_len + 1); | ||
438 | niro | 816 | xread(zip_fd, dst_fn, zip_header.formatted.filename_len); |
439 | niro | 532 | |
440 | /* Skip extra header bytes */ | ||
441 | niro | 816 | unzip_skip(zip_header.formatted.extra_len); |
442 | niro | 532 | |
443 | /* Filter zip entries */ | ||
444 | niro | 816 | if (find_list_entry(zreject, dst_fn) |
445 | || (zaccept && !find_list_entry(zaccept, dst_fn)) | ||
446 | ) { /* Skip entry */ | ||
447 | niro | 532 | i = 'n'; |
448 | |||
449 | } else { /* Extract entry */ | ||
450 | niro | 816 | if (listing) { /* List entry */ |
451 | if (verbose) { | ||
452 | unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); | ||
453 | printf("%9u %02u-%02u-%02u %02u:%02u %s\n", | ||
454 | niro | 532 | zip_header.formatted.ucmpsize, |
455 | (dostime & 0x01e00000) >> 21, | ||
456 | (dostime & 0x001f0000) >> 16, | ||
457 | (((dostime & 0xfe000000) >> 25) + 1980) % 100, | ||
458 | (dostime & 0x0000f800) >> 11, | ||
459 | (dostime & 0x000007e0) >> 5, | ||
460 | dst_fn); | ||
461 | niro | 816 | total_size += zip_header.formatted.ucmpsize; |
462 | } else { | ||
463 | /* short listing -- filenames only */ | ||
464 | puts(dst_fn); | ||
465 | } | ||
466 | niro | 532 | i = 'n'; |
467 | } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ | ||
468 | i = -1; | ||
469 | } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ | ||
470 | if (stat(dst_fn, &stat_buf) == -1) { | ||
471 | if (errno != ENOENT) { | ||
472 | niro | 816 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
473 | niro | 532 | } |
474 | niro | 816 | if (verbose) { |
475 | niro | 532 | printf(" creating: %s\n", dst_fn); |
476 | } | ||
477 | unzip_create_leading_dirs(dst_fn); | ||
478 | if (bb_make_directory(dst_fn, 0777, 0)) { | ||
479 | bb_error_msg_and_die("exiting"); | ||
480 | } | ||
481 | } else { | ||
482 | if (!S_ISDIR(stat_buf.st_mode)) { | ||
483 | bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); | ||
484 | } | ||
485 | } | ||
486 | i = 'n'; | ||
487 | |||
488 | } else { /* Extract file */ | ||
489 | niro | 816 | check_file: |
490 | niro | 532 | if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ |
491 | if (errno != ENOENT) { | ||
492 | niro | 816 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
493 | niro | 532 | } |
494 | i = 'y'; | ||
495 | } else { /* File already exists */ | ||
496 | niro | 816 | if (overwrite == O_NEVER) { |
497 | niro | 532 | i = 'n'; |
498 | } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ | ||
499 | niro | 816 | if (overwrite == O_ALWAYS) { |
500 | niro | 532 | i = 'y'; |
501 | } else { | ||
502 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); | ||
503 | niro | 816 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
504 | bb_perror_msg_and_die("can't read input"); | ||
505 | niro | 532 | } |
506 | i = key_buf[0]; | ||
507 | } | ||
508 | } else { /* File is not regular file */ | ||
509 | niro | 816 | bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); |
510 | niro | 532 | } |
511 | } | ||
512 | } | ||
513 | } | ||
514 | |||
515 | switch (i) { | ||
516 | case 'A': | ||
517 | niro | 816 | overwrite = O_ALWAYS; |
518 | niro | 532 | case 'y': /* Open file and fall into unzip */ |
519 | unzip_create_leading_dirs(dst_fn); | ||
520 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); | ||
521 | case -1: /* Unzip */ | ||
522 | niro | 816 | if (verbose) { |
523 | niro | 532 | printf(" inflating: %s\n", dst_fn); |
524 | } | ||
525 | niro | 816 | unzip_extract(&zip_header, dst_fd); |
526 | niro | 532 | if (dst_fd != STDOUT_FILENO) { |
527 | /* closing STDOUT is potentially bad for future business */ | ||
528 | close(dst_fd); | ||
529 | } | ||
530 | break; | ||
531 | |||
532 | case 'N': | ||
533 | niro | 816 | overwrite = O_NEVER; |
534 | niro | 532 | case 'n': |
535 | /* Skip entry data */ | ||
536 | niro | 816 | unzip_skip(zip_header.formatted.cmpsize); |
537 | niro | 532 | break; |
538 | |||
539 | case 'r': | ||
540 | /* Prompt for new name */ | ||
541 | printf("new name: "); | ||
542 | niro | 816 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
543 | bb_perror_msg_and_die("can't read input"); | ||
544 | niro | 532 | } |
545 | free(dst_fn); | ||
546 | dst_fn = xstrdup(key_buf); | ||
547 | chomp(dst_fn); | ||
548 | niro | 816 | goto check_file; |
549 | niro | 532 | |
550 | default: | ||
551 | printf("error: invalid response [%c]\n",(char)i); | ||
552 | niro | 816 | goto check_file; |
553 | niro | 532 | } |
554 | |||
555 | niro | 816 | total_entries++; |
556 | niro | 532 | } |
557 | |||
558 | niro | 816 | if (listing && verbose) { |
559 | niro | 532 | printf(" -------- -------\n" |
560 | niro | 816 | "%9d %d files\n", |
561 | total_size, total_entries); | ||
562 | niro | 532 | } |
563 | |||
564 | niro | 816 | return 0; |
565 | niro | 532 | } |