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