1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#include "libbb.h"
35#include "archive.h"
36
37enum {
38#if BB_BIG_ENDIAN
39 ZIP_FILEHEADER_MAGIC = 0x504b0304,
40 ZIP_CDF_MAGIC = 0x504b0102,
41 ZIP_CDE_MAGIC = 0x504b0506,
42 ZIP_DD_MAGIC = 0x504b0708,
43#else
44 ZIP_FILEHEADER_MAGIC = 0x04034b50,
45 ZIP_CDF_MAGIC = 0x02014b50,
46 ZIP_CDE_MAGIC = 0x06054b50,
47 ZIP_DD_MAGIC = 0x08074b50,
48#endif
49};
50
51#define ZIP_HEADER_LEN 26
52
53typedef union {
54 uint8_t raw[ZIP_HEADER_LEN];
55 struct {
56 uint16_t version;
57 uint16_t zip_flags;
58 uint16_t method;
59 uint16_t modtime;
60 uint16_t moddate;
61 uint32_t crc32 PACKED;
62 uint32_t cmpsize PACKED;
63 uint32_t ucmpsize PACKED;
64 uint16_t filename_len;
65 uint16_t extra_len;
66 } formatted PACKED;
67} zip_header_t;
68
69
70
71
72
73struct BUG_zip_header_must_be_26_bytes {
74 char BUG_zip_header_must_be_26_bytes[
75 offsetof(zip_header_t, formatted.extra_len) + 2
76 == ZIP_HEADER_LEN ? 1 : -1];
77};
78
79#define FIX_ENDIANNESS_ZIP(zip_header) do { \
80 (zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \
81 (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \
82 (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \
83 (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \
84 (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \
85 (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \
86 (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \
87 (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \
88 (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \
89} while (0)
90
91#define CDF_HEADER_LEN 42
92
93typedef union {
94 uint8_t raw[CDF_HEADER_LEN];
95 struct {
96
97 uint16_t version_made_by;
98 uint16_t version_needed;
99 uint16_t cdf_flags;
100 uint16_t method;
101 uint16_t mtime;
102 uint16_t mdate;
103 uint32_t crc32;
104 uint32_t cmpsize;
105 uint32_t ucmpsize;
106 uint16_t file_name_length;
107 uint16_t extra_field_length;
108 uint16_t file_comment_length;
109 uint16_t disk_number_start;
110 uint16_t internal_file_attributes;
111 uint32_t external_file_attributes PACKED;
112 uint32_t relative_offset_of_local_header PACKED;
113 } formatted PACKED;
114} cdf_header_t;
115
116struct BUG_cdf_header_must_be_42_bytes {
117 char BUG_cdf_header_must_be_42_bytes[
118 offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4
119 == CDF_HEADER_LEN ? 1 : -1];
120};
121
122#define FIX_ENDIANNESS_CDF(cdf_header) do { \
123 (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \
124 (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \
125 (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \
126 (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \
127 (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \
128 (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \
129 IF_DESKTOP( \
130 (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \
131 (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \
132 ) \
133} while (0)
134
135#define CDE_HEADER_LEN 16
136
137typedef union {
138 uint8_t raw[CDE_HEADER_LEN];
139 struct {
140
141 uint16_t this_disk_no;
142 uint16_t disk_with_cdf_no;
143 uint16_t cdf_entries_on_this_disk;
144 uint16_t cdf_entries_total;
145 uint32_t cdf_size;
146 uint32_t cdf_offset;
147
148
149 } formatted PACKED;
150} cde_header_t;
151
152struct BUG_cde_header_must_be_16_bytes {
153 char BUG_cde_header_must_be_16_bytes[
154 sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1];
155};
156
157#define FIX_ENDIANNESS_CDE(cde_header) do { \
158 (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \
159} while (0)
160
161enum { zip_fd = 3 };
162
163
164#if ENABLE_DESKTOP
165
166#define PEEK_FROM_END 16384
167
168
169static uint32_t find_cdf_offset(void)
170{
171 cde_header_t cde_header;
172 unsigned char *p;
173 off_t end;
174 unsigned char *buf = xzalloc(PEEK_FROM_END);
175
176 end = xlseek(zip_fd, 0, SEEK_END);
177 end -= PEEK_FROM_END;
178 if (end < 0)
179 end = 0;
180 xlseek(zip_fd, end, SEEK_SET);
181 full_read(zip_fd, buf, PEEK_FROM_END);
182
183 p = buf;
184 while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
185 if (*p != 'P') {
186 p++;
187 continue;
188 }
189 if (*++p != 'K')
190 continue;
191 if (*++p != 5)
192 continue;
193 if (*++p != 6)
194 continue;
195
196 memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
197 FIX_ENDIANNESS_CDE(cde_header);
198 free(buf);
199 return cde_header.formatted.cdf_offset;
200 }
201
202 bb_error_msg_and_die("can't find file table");
203};
204
205static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
206{
207 off_t org;
208
209 org = xlseek(zip_fd, 0, SEEK_CUR);
210
211 if (!cdf_offset)
212 cdf_offset = find_cdf_offset();
213
214 xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
215 xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
216 FIX_ENDIANNESS_CDF(*cdf_ptr);
217 cdf_offset += 4 + CDF_HEADER_LEN
218 + cdf_ptr->formatted.file_name_length
219 + cdf_ptr->formatted.extra_field_length
220 + cdf_ptr->formatted.file_comment_length;
221
222 xlseek(zip_fd, org, SEEK_SET);
223 return cdf_offset;
224};
225#endif
226
227static void unzip_skip(off_t skip)
228{
229 if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
230 bb_copyfd_exact_size(zip_fd, -1, skip);
231}
232
233static void unzip_create_leading_dirs(const char *fn)
234{
235
236 char *name = xstrdup(fn);
237 if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
238 bb_error_msg_and_die("exiting");
239 }
240 free(name);
241}
242
243static void unzip_extract(zip_header_t *zip_header, int dst_fd)
244{
245 if (zip_header->formatted.method == 0) {
246
247 off_t size = zip_header->formatted.ucmpsize;
248 if (size)
249 bb_copyfd_exact_size(zip_fd, dst_fd, size);
250 } else {
251
252 inflate_unzip_result res;
253 if (inflate_unzip(&res, zip_header->formatted.cmpsize, zip_fd, dst_fd) < 0)
254 bb_error_msg_and_die("inflate error");
255
256 if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) {
257 bb_error_msg_and_die("crc error");
258 }
259
260 if (zip_header->formatted.ucmpsize != res.bytes_out) {
261
262
263 bb_error_msg("bad length");
264 }
265 }
266}
267
268int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
269int unzip_main(int argc, char **argv)
270{
271 enum { O_PROMPT, O_NEVER, O_ALWAYS };
272
273 zip_header_t zip_header;
274 smallint quiet = 0;
275 IF_NOT_DESKTOP(const) smallint verbose = 0;
276 smallint listing = 0;
277 smallint overwrite = O_PROMPT;
278#if ENABLE_DESKTOP
279 uint32_t cdf_offset;
280#endif
281 unsigned long total_usize;
282 unsigned long total_size;
283 unsigned total_entries;
284 int dst_fd = -1;
285 char *src_fn = NULL;
286 char *dst_fn = NULL;
287 llist_t *zaccept = NULL;
288 llist_t *zreject = NULL;
289 char *base_dir = NULL;
290 int i, opt;
291 int opt_range = 0;
292 char key_buf[80];
293 struct stat stat_buf;
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337 while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) {
338 switch (opt_range) {
339 case 0:
340 switch (opt) {
341 case 'l':
342 listing = 1;
343 break;
344
345 case 'n':
346 overwrite = O_NEVER;
347 break;
348
349 case 'o':
350 overwrite = O_ALWAYS;
351 break;
352
353 case 'p':
354 dst_fd = STDOUT_FILENO;
355
356 case 'q':
357 quiet++;
358 break;
359
360 case 'v':
361 IF_DESKTOP(verbose++;)
362 listing = 1;
363 break;
364
365 case 1:
366
367 src_fn = xmalloc(strlen(optarg) + 5);
368 strcpy(src_fn, optarg);
369 opt_range++;
370 break;
371
372 default:
373 bb_show_usage();
374 }
375 break;
376
377 case 1:
378 if (opt == 1) {
379 llist_add_to(&zaccept, optarg);
380 break;
381 }
382 if (opt == 'd') {
383 base_dir = optarg;
384 opt_range += 2;
385 break;
386 }
387 if (opt == 'x') {
388 opt_range++;
389 break;
390 }
391 bb_show_usage();
392
393 case 2 :
394 if (opt == 1) {
395 llist_add_to(&zreject, optarg);
396 break;
397 }
398 if (opt == 'd') {
399 base_dir = optarg;
400 opt_range++;
401 break;
402 }
403
404
405 default:
406 bb_show_usage();
407 }
408 }
409
410 if (src_fn == NULL) {
411 bb_show_usage();
412 }
413
414
415 if (LONE_DASH(src_fn)) {
416 xdup2(STDIN_FILENO, zip_fd);
417
418 if (overwrite == O_PROMPT)
419 overwrite = O_NEVER;
420 } else {
421 static const char extn[][5] = {"", ".zip", ".ZIP"};
422 int orig_src_fn_len = strlen(src_fn);
423 int src_fd = -1;
424
425 for (i = 0; (i < 3) && (src_fd == -1); i++) {
426 strcpy(src_fn + orig_src_fn_len, extn[i]);
427 src_fd = open(src_fn, O_RDONLY);
428 }
429 if (src_fd == -1) {
430 src_fn[orig_src_fn_len] = '\0';
431 bb_error_msg_and_die("can't open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn);
432 }
433 xmove_fd(src_fd, zip_fd);
434 }
435
436
437 if (base_dir)
438 xchdir(base_dir);
439
440 if (quiet <= 1) {
441 if (quiet == 0)
442 printf("Archive: %s\n", src_fn);
443 if (listing) {
444 puts(verbose ?
445 " Length Method Size Ratio Date Time CRC-32 Name\n"
446 "-------- ------ ------- ----- ---- ---- ------ ----"
447 :
448 " Length Date Time Name\n"
449 " -------- ---- ---- ----"
450 );
451 }
452 }
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473 total_usize = 0;
474 total_size = 0;
475 total_entries = 0;
476#if ENABLE_DESKTOP
477 cdf_offset = 0;
478#endif
479 while (1) {
480 uint32_t magic;
481 mode_t dir_mode = 0777;
482#if ENABLE_DESKTOP
483 mode_t file_mode = 0666;
484#endif
485
486
487 xread(zip_fd, &magic, 4);
488
489 if (magic == ZIP_CDF_MAGIC)
490 break;
491#if ENABLE_DESKTOP
492
493 if (magic == ZIP_DD_MAGIC) {
494
495 unzip_skip(3 * 4);
496 continue;
497 }
498#endif
499 if (magic != ZIP_FILEHEADER_MAGIC)
500 bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
501
502
503 xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN);
504 FIX_ENDIANNESS_ZIP(zip_header);
505 if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) {
506 bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method);
507 }
508#if !ENABLE_DESKTOP
509 if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) {
510 bb_error_msg_and_die("zip flags 1 and 8 are not supported");
511 }
512#else
513 if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) {
514
515 bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
516 }
517
518 {
519 cdf_header_t cdf_header;
520 cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
521 if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
522
523
524 zip_header.formatted.crc32 = cdf_header.formatted.crc32;
525 zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize;
526 zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize;
527 }
528 if ((cdf_header.formatted.version_made_by >> 8) == 3) {
529
530 dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
531 }
532 }
533#endif
534
535
536 free(dst_fn);
537 dst_fn = xzalloc(zip_header.formatted.filename_len + 1);
538 xread(zip_fd, dst_fn, zip_header.formatted.filename_len);
539
540
541 unzip_skip(zip_header.formatted.extra_len);
542
543
544 if (find_list_entry(zreject, dst_fn)
545 || (zaccept && !find_list_entry(zaccept, dst_fn))
546 ) {
547 i = 'n';
548
549 } else {
550 if (listing) {
551 unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
552 if (!verbose) {
553
554
555 printf( "%9u %02u-%02u-%02u %02u:%02u %s\n",
556 (unsigned)zip_header.formatted.ucmpsize,
557 (dostime & 0x01e00000) >> 21,
558 (dostime & 0x001f0000) >> 16,
559 (((dostime & 0xfe000000) >> 25) + 1980) % 100,
560 (dostime & 0x0000f800) >> 11,
561 (dostime & 0x000007e0) >> 5,
562 dst_fn);
563 total_usize += zip_header.formatted.ucmpsize;
564 } else {
565 unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize;
566 percents = percents * 100;
567 if (zip_header.formatted.ucmpsize)
568 percents /= zip_header.formatted.ucmpsize;
569
570
571 printf( "%8u Defl:N" "%9u%4u%% %02u-%02u-%02u %02u:%02u %08x %s\n",
572 (unsigned)zip_header.formatted.ucmpsize,
573 (unsigned)zip_header.formatted.cmpsize,
574 (unsigned)percents,
575 (dostime & 0x01e00000) >> 21,
576 (dostime & 0x001f0000) >> 16,
577 (((dostime & 0xfe000000) >> 25) + 1980) % 100,
578 (dostime & 0x0000f800) >> 11,
579 (dostime & 0x000007e0) >> 5,
580 zip_header.formatted.crc32,
581 dst_fn);
582 total_usize += zip_header.formatted.ucmpsize;
583 total_size += zip_header.formatted.cmpsize;
584 }
585 i = 'n';
586 } else if (dst_fd == STDOUT_FILENO) {
587 i = -1;
588 } else if (last_char_is(dst_fn, '/')) {
589 if (stat(dst_fn, &stat_buf) == -1) {
590 if (errno != ENOENT) {
591 bb_perror_msg_and_die("can't stat '%s'", dst_fn);
592 }
593 if (!quiet) {
594 printf(" creating: %s\n", dst_fn);
595 }
596 unzip_create_leading_dirs(dst_fn);
597 if (bb_make_directory(dst_fn, dir_mode, 0)) {
598 bb_error_msg_and_die("exiting");
599 }
600 } else {
601 if (!S_ISDIR(stat_buf.st_mode)) {
602 bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
603 }
604 }
605 i = 'n';
606
607 } else {
608 check_file:
609 if (stat(dst_fn, &stat_buf) == -1) {
610 if (errno != ENOENT) {
611 bb_perror_msg_and_die("can't stat '%s'", dst_fn);
612 }
613 i = 'y';
614 } else {
615 if (overwrite == O_NEVER) {
616 i = 'n';
617 } else if (S_ISREG(stat_buf.st_mode)) {
618 if (overwrite == O_ALWAYS) {
619 i = 'y';
620 } else {
621 printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
622 if (!fgets(key_buf, sizeof(key_buf), stdin)) {
623 bb_perror_msg_and_die("can't read input");
624 }
625 i = key_buf[0];
626 }
627 } else {
628 bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn);
629 }
630 }
631 }
632 }
633
634 switch (i) {
635 case 'A':
636 overwrite = O_ALWAYS;
637 case 'y':
638 unzip_create_leading_dirs(dst_fn);
639#if ENABLE_DESKTOP
640 dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
641#else
642 dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC);
643#endif
644 case -1:
645 if (!quiet) {
646 printf(" inflating: %s\n", dst_fn);
647 }
648 unzip_extract(&zip_header, dst_fd);
649 if (dst_fd != STDOUT_FILENO) {
650
651 close(dst_fd);
652 }
653 break;
654
655 case 'N':
656 overwrite = O_NEVER;
657 case 'n':
658
659 unzip_skip(zip_header.formatted.cmpsize);
660 break;
661
662 case 'r':
663
664 printf("new name: ");
665 if (!fgets(key_buf, sizeof(key_buf), stdin)) {
666 bb_perror_msg_and_die("can't read input");
667 }
668 free(dst_fn);
669 dst_fn = xstrdup(key_buf);
670 chomp(dst_fn);
671 goto check_file;
672
673 default:
674 printf("error: invalid response [%c]\n", (char)i);
675 goto check_file;
676 }
677
678 total_entries++;
679 }
680
681 if (listing && quiet <= 1) {
682 if (!verbose) {
683
684
685 printf( " -------- -------\n"
686 "%9lu" " %u files\n",
687 total_usize, total_entries);
688 } else {
689 unsigned long percents = total_usize - total_size;
690 percents = percents * 100;
691 if (total_usize)
692 percents /= total_usize;
693
694
695 printf( "-------- ------- --- -------\n"
696 "%8lu" "%17lu%4u%% %u files\n",
697 total_usize, total_size, (unsigned)percents,
698 total_entries);
699 }
700 }
701
702 return 0;
703}
704