25 |
enum { |
enum { |
26 |
#if BB_BIG_ENDIAN |
#if BB_BIG_ENDIAN |
27 |
ZIP_FILEHEADER_MAGIC = 0x504b0304, |
ZIP_FILEHEADER_MAGIC = 0x504b0304, |
28 |
ZIP_CDS_MAGIC = 0x504b0102, |
ZIP_CDF_MAGIC = 0x504b0102, /* central directory's file header */ |
29 |
ZIP_CDE_MAGIC = 0x504b0506, |
ZIP_CDE_MAGIC = 0x504b0506, /* "end of central directory" record */ |
30 |
ZIP_DD_MAGIC = 0x504b0708, |
ZIP_DD_MAGIC = 0x504b0708, |
31 |
#else |
#else |
32 |
ZIP_FILEHEADER_MAGIC = 0x04034b50, |
ZIP_FILEHEADER_MAGIC = 0x04034b50, |
33 |
ZIP_CDS_MAGIC = 0x02014b50, |
ZIP_CDF_MAGIC = 0x02014b50, |
34 |
ZIP_CDE_MAGIC = 0x06054b50, |
ZIP_CDE_MAGIC = 0x06054b50, |
35 |
ZIP_DD_MAGIC = 0x08074b50, |
ZIP_DD_MAGIC = 0x08074b50, |
36 |
#endif |
#endif |
42 |
uint8_t raw[ZIP_HEADER_LEN]; |
uint8_t raw[ZIP_HEADER_LEN]; |
43 |
struct { |
struct { |
44 |
uint16_t version; /* 0-1 */ |
uint16_t version; /* 0-1 */ |
45 |
uint16_t flags; /* 2-3 */ |
uint16_t zip_flags; /* 2-3 */ |
46 |
uint16_t method; /* 4-5 */ |
uint16_t method; /* 4-5 */ |
47 |
uint16_t modtime; /* 6-7 */ |
uint16_t modtime; /* 6-7 */ |
48 |
uint16_t moddate; /* 8-9 */ |
uint16_t moddate; /* 8-9 */ |
66 |
|
|
67 |
#define FIX_ENDIANNESS_ZIP(zip_header) do { \ |
#define FIX_ENDIANNESS_ZIP(zip_header) do { \ |
68 |
(zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ |
(zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ |
|
(zip_header).formatted.flags = SWAP_LE16((zip_header).formatted.flags ); \ |
|
69 |
(zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ |
(zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ |
70 |
(zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ |
(zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ |
71 |
(zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ |
(zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ |
76 |
(zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ |
(zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ |
77 |
} while (0) |
} while (0) |
78 |
|
|
79 |
#define CDS_HEADER_LEN 42 |
#define CDF_HEADER_LEN 42 |
80 |
|
|
81 |
typedef union { |
typedef union { |
82 |
uint8_t raw[CDS_HEADER_LEN]; |
uint8_t raw[CDF_HEADER_LEN]; |
83 |
struct { |
struct { |
84 |
/* uint32_t signature; 50 4b 01 02 */ |
/* uint32_t signature; 50 4b 01 02 */ |
85 |
uint16_t version_made_by; /* 0-1 */ |
uint16_t version_made_by; /* 0-1 */ |
86 |
uint16_t version_needed; /* 2-3 */ |
uint16_t version_needed; /* 2-3 */ |
87 |
uint16_t cds_flags; /* 4-5 */ |
uint16_t cdf_flags; /* 4-5 */ |
88 |
uint16_t method; /* 6-7 */ |
uint16_t method; /* 6-7 */ |
89 |
uint16_t mtime; /* 8-9 */ |
uint16_t mtime; /* 8-9 */ |
90 |
uint16_t mdate; /* 10-11 */ |
uint16_t mdate; /* 10-11 */ |
99 |
uint32_t external_file_attributes PACKED; /* 34-37 */ |
uint32_t external_file_attributes PACKED; /* 34-37 */ |
100 |
uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ |
uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ |
101 |
} formatted PACKED; |
} formatted PACKED; |
102 |
} cds_header_t; |
} cdf_header_t; |
103 |
|
|
104 |
struct BUG_cds_header_must_be_42_bytes { |
struct BUG_cdf_header_must_be_42_bytes { |
105 |
char BUG_cds_header_must_be_42_bytes[ |
char BUG_cdf_header_must_be_42_bytes[ |
106 |
offsetof(cds_header_t, formatted.relative_offset_of_local_header) + 4 |
offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 |
107 |
== CDS_HEADER_LEN ? 1 : -1]; |
== CDF_HEADER_LEN ? 1 : -1]; |
108 |
}; |
}; |
109 |
|
|
110 |
#define FIX_ENDIANNESS_CDS(cds_header) do { \ |
#define FIX_ENDIANNESS_CDF(cdf_header) do { \ |
111 |
(cds_header).formatted.crc32 = SWAP_LE32((cds_header).formatted.crc32 ); \ |
(cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ |
112 |
(cds_header).formatted.cmpsize = SWAP_LE32((cds_header).formatted.cmpsize ); \ |
(cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ |
113 |
(cds_header).formatted.ucmpsize = SWAP_LE32((cds_header).formatted.ucmpsize ); \ |
(cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ |
114 |
(cds_header).formatted.file_name_length = SWAP_LE16((cds_header).formatted.file_name_length); \ |
(cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ |
115 |
(cds_header).formatted.extra_field_length = SWAP_LE16((cds_header).formatted.extra_field_length); \ |
(cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ |
116 |
(cds_header).formatted.file_comment_length = SWAP_LE16((cds_header).formatted.file_comment_length); \ |
(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) |
} while (0) |
122 |
|
|
123 |
#define CDE_HEADER_LEN 16 |
#define CDE_HEADER_LEN 16 |
127 |
struct { |
struct { |
128 |
/* uint32_t signature; 50 4b 05 06 */ |
/* uint32_t signature; 50 4b 05 06 */ |
129 |
uint16_t this_disk_no; |
uint16_t this_disk_no; |
130 |
uint16_t disk_with_cds_no; |
uint16_t disk_with_cdf_no; |
131 |
uint16_t cds_entries_on_this_disk; |
uint16_t cdf_entries_on_this_disk; |
132 |
uint16_t cds_entries_total; |
uint16_t cdf_entries_total; |
133 |
uint32_t cds_size; |
uint32_t cdf_size; |
134 |
uint32_t cds_offset; |
uint32_t cdf_offset; |
135 |
/* uint16_t file_comment_length; */ |
/* uint16_t file_comment_length; */ |
136 |
/* .ZIP file comment (variable size) */ |
/* .ZIP file comment (variable size) */ |
137 |
} formatted PACKED; |
} formatted PACKED; |
143 |
}; |
}; |
144 |
|
|
145 |
#define FIX_ENDIANNESS_CDE(cde_header) do { \ |
#define FIX_ENDIANNESS_CDE(cde_header) do { \ |
146 |
(cde_header).formatted.cds_offset = SWAP_LE32((cde_header).formatted.cds_offset); \ |
(cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ |
147 |
} while (0) |
} while (0) |
148 |
|
|
149 |
enum { zip_fd = 3 }; |
enum { zip_fd = 3 }; |
150 |
|
|
151 |
|
|
152 |
#if ENABLE_DESKTOP |
#if ENABLE_DESKTOP |
153 |
|
|
154 |
|
#define PEEK_FROM_END 16384 |
155 |
|
|
156 |
/* NB: does not preserve file position! */ |
/* NB: does not preserve file position! */ |
157 |
static uint32_t find_cds_offset(void) |
static uint32_t find_cdf_offset(void) |
158 |
{ |
{ |
|
unsigned char buf[1024]; |
|
159 |
cde_header_t cde_header; |
cde_header_t cde_header; |
160 |
unsigned char *p; |
unsigned char *p; |
161 |
off_t end; |
off_t end; |
162 |
|
unsigned char *buf = xzalloc(PEEK_FROM_END); |
163 |
|
|
164 |
end = xlseek(zip_fd, 0, SEEK_END); |
end = xlseek(zip_fd, 0, SEEK_END); |
165 |
if (end < 1024) |
end -= PEEK_FROM_END; |
166 |
end = 1024; |
if (end < 0) |
167 |
end -= 1024; |
end = 0; |
168 |
xlseek(zip_fd, end, SEEK_SET); |
xlseek(zip_fd, end, SEEK_SET); |
169 |
full_read(zip_fd, buf, 1024); |
full_read(zip_fd, buf, PEEK_FROM_END); |
170 |
|
|
171 |
p = buf; |
p = buf; |
172 |
while (p <= buf + 1024 - CDE_HEADER_LEN - 4) { |
while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { |
173 |
if (*p != 'P') { |
if (*p != 'P') { |
174 |
p++; |
p++; |
175 |
continue; |
continue; |
183 |
/* we found CDE! */ |
/* we found CDE! */ |
184 |
memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); |
memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); |
185 |
FIX_ENDIANNESS_CDE(cde_header); |
FIX_ENDIANNESS_CDE(cde_header); |
186 |
return cde_header.formatted.cds_offset; |
free(buf); |
187 |
|
return cde_header.formatted.cdf_offset; |
188 |
} |
} |
189 |
|
//free(buf); |
190 |
bb_error_msg_and_die("can't find file table"); |
bb_error_msg_and_die("can't find file table"); |
191 |
}; |
}; |
192 |
|
|
193 |
static uint32_t read_next_cds(int count_m1, uint32_t cds_offset, cds_header_t *cds_ptr) |
static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) |
194 |
{ |
{ |
195 |
off_t org; |
off_t org; |
196 |
|
|
197 |
org = xlseek(zip_fd, 0, SEEK_CUR); |
org = xlseek(zip_fd, 0, SEEK_CUR); |
198 |
|
|
199 |
if (!cds_offset) |
if (!cdf_offset) |
200 |
cds_offset = find_cds_offset(); |
cdf_offset = find_cdf_offset(); |
201 |
|
|
202 |
while (count_m1-- >= 0) { |
xlseek(zip_fd, cdf_offset + 4, SEEK_SET); |
203 |
xlseek(zip_fd, cds_offset + 4, SEEK_SET); |
xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); |
204 |
xread(zip_fd, cds_ptr->raw, CDS_HEADER_LEN); |
FIX_ENDIANNESS_CDF(*cdf_ptr); |
205 |
FIX_ENDIANNESS_CDS(*cds_ptr); |
cdf_offset += 4 + CDF_HEADER_LEN |
206 |
cds_offset += 4 + CDS_HEADER_LEN |
+ cdf_ptr->formatted.file_name_length |
207 |
+ cds_ptr->formatted.file_name_length |
+ cdf_ptr->formatted.extra_field_length |
208 |
+ cds_ptr->formatted.extra_field_length |
+ cdf_ptr->formatted.file_comment_length; |
|
+ cds_ptr->formatted.file_comment_length; |
|
|
} |
|
209 |
|
|
210 |
xlseek(zip_fd, org, SEEK_SET); |
xlseek(zip_fd, org, SEEK_SET); |
211 |
return cds_offset; |
return cdf_offset; |
212 |
}; |
}; |
213 |
#endif |
#endif |
214 |
|
|
264 |
smallint listing = 0; |
smallint listing = 0; |
265 |
smallint overwrite = O_PROMPT; |
smallint overwrite = O_PROMPT; |
266 |
#if ENABLE_DESKTOP |
#if ENABLE_DESKTOP |
267 |
uint32_t cds_offset; |
uint32_t cdf_offset; |
|
unsigned cds_entries; |
|
268 |
#endif |
#endif |
269 |
unsigned long total_usize; |
unsigned long total_usize; |
270 |
unsigned long total_size; |
unsigned long total_size; |
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; |
total_usize = 0; |
463 |
total_size = 0; |
total_size = 0; |
464 |
total_entries = 0; |
total_entries = 0; |
465 |
#if ENABLE_DESKTOP |
#if ENABLE_DESKTOP |
466 |
cds_entries = 0; |
cdf_offset = 0; |
|
cds_offset = 0; |
|
467 |
#endif |
#endif |
468 |
while (1) { |
while (1) { |
469 |
uint32_t magic; |
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 */ |
/* Check magic number */ |
476 |
xread(zip_fd, &magic, 4); |
xread(zip_fd, &magic, 4); |
477 |
/* Central directory? It's at the end, so exit */ |
/* Central directory? It's at the end, so exit */ |
478 |
if (magic == ZIP_CDS_MAGIC) |
if (magic == ZIP_CDF_MAGIC) |
479 |
break; |
break; |
480 |
#if ENABLE_DESKTOP |
#if ENABLE_DESKTOP |
481 |
/* Data descriptor? It was a streaming file, go on */ |
/* Data descriptor? It was a streaming file, go on */ |
495 |
bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); |
bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); |
496 |
} |
} |
497 |
#if !ENABLE_DESKTOP |
#if !ENABLE_DESKTOP |
498 |
if (zip_header.formatted.flags & 0x0009) { |
if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { |
499 |
bb_error_msg_and_die("zip flags 1 and 8 are not supported"); |
bb_error_msg_and_die("zip flags 1 and 8 are not supported"); |
500 |
} |
} |
501 |
#else |
#else |
502 |
if (zip_header.formatted.flags & 0x0001) { |
if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { |
503 |
/* 0x0001 - encrypted */ |
/* 0x0001 - encrypted */ |
504 |
bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); |
bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); |
505 |
} |
} |
506 |
if (zip_header.formatted.flags & 0x0008) { |
|
507 |
cds_header_t cds_header; |
{ |
508 |
/* 0x0008 - streaming. [u]cmpsize can be reliably gotten |
cdf_header_t cdf_header; |
509 |
* only from Central Directory. See unzip_doc.txt */ |
cdf_offset = read_next_cdf(cdf_offset, &cdf_header); |
510 |
cds_offset = read_next_cds(total_entries - cds_entries, cds_offset, &cds_header); |
if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { |
511 |
cds_entries = total_entries + 1; |
/* 0x0008 - streaming. [u]cmpsize can be reliably gotten |
512 |
zip_header.formatted.crc32 = cds_header.formatted.crc32; |
* only from Central Directory. See unzip_doc.txt */ |
513 |
zip_header.formatted.cmpsize = cds_header.formatted.cmpsize; |
zip_header.formatted.crc32 = cdf_header.formatted.crc32; |
514 |
zip_header.formatted.ucmpsize = cds_header.formatted.ucmpsize; |
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 |
#endif |
523 |
|
|
583 |
printf(" creating: %s\n", dst_fn); |
printf(" creating: %s\n", dst_fn); |
584 |
} |
} |
585 |
unzip_create_leading_dirs(dst_fn); |
unzip_create_leading_dirs(dst_fn); |
586 |
if (bb_make_directory(dst_fn, 0777, 0)) { |
if (bb_make_directory(dst_fn, dir_mode, 0)) { |
587 |
bb_error_msg_and_die("exiting"); |
bb_error_msg_and_die("exiting"); |
588 |
} |
} |
589 |
} else { |
} else { |
625 |
overwrite = O_ALWAYS; |
overwrite = O_ALWAYS; |
626 |
case 'y': /* Open file and fall into unzip */ |
case 'y': /* Open file and fall into unzip */ |
627 |
unzip_create_leading_dirs(dst_fn); |
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); |
dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); |
632 |
|
#endif |
633 |
case -1: /* Unzip */ |
case -1: /* Unzip */ |
634 |
if (!quiet) { |
if (!quiet) { |
635 |
printf(" inflating: %s\n", dst_fn); |
printf(" inflating: %s\n", dst_fn); |