unzip: survive lack of CDF on non-streaming zip files
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
1390a010b6
commit
26cd90c7fd
@ -172,6 +172,9 @@ enum { zip_fd = 3 };
|
|||||||
*/
|
*/
|
||||||
#define PEEK_FROM_END (64*1024)
|
#define PEEK_FROM_END (64*1024)
|
||||||
|
|
||||||
|
/* This value means that we failed to find CDF */
|
||||||
|
#define BAD_CDF_OFFSET ((uint32_t)0xffffffff)
|
||||||
|
|
||||||
/* NB: does not preserve file position! */
|
/* NB: does not preserve file position! */
|
||||||
static uint32_t find_cdf_offset(void)
|
static uint32_t find_cdf_offset(void)
|
||||||
{
|
{
|
||||||
@ -187,6 +190,7 @@ static uint32_t find_cdf_offset(void)
|
|||||||
xlseek(zip_fd, end, SEEK_SET);
|
xlseek(zip_fd, end, SEEK_SET);
|
||||||
full_read(zip_fd, buf, PEEK_FROM_END);
|
full_read(zip_fd, buf, PEEK_FROM_END);
|
||||||
|
|
||||||
|
cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
|
||||||
p = buf;
|
p = buf;
|
||||||
while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
|
while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
|
||||||
if (*p != 'P') {
|
if (*p != 'P') {
|
||||||
@ -202,11 +206,17 @@ static uint32_t find_cdf_offset(void)
|
|||||||
/* we found CDE! */
|
/* we found CDE! */
|
||||||
memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
|
memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
|
||||||
FIX_ENDIANNESS_CDE(cde_header);
|
FIX_ENDIANNESS_CDE(cde_header);
|
||||||
free(buf);
|
/*
|
||||||
return cde_header.formatted.cdf_offset;
|
* I've seen .ZIP files with seemingly valid CDEs
|
||||||
|
* where cdf_offset points past EOF - ??
|
||||||
|
* Ignore such CDEs:
|
||||||
|
*/
|
||||||
|
if (cde_header.formatted.cdf_offset < end + (p - buf))
|
||||||
|
break;
|
||||||
|
cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
|
||||||
}
|
}
|
||||||
//free(buf);
|
free(buf);
|
||||||
bb_error_msg_and_die("can't find file table");
|
return cde_header.formatted.cdf_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
|
static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
|
||||||
@ -218,13 +228,15 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
|
|||||||
if (!cdf_offset)
|
if (!cdf_offset)
|
||||||
cdf_offset = find_cdf_offset();
|
cdf_offset = find_cdf_offset();
|
||||||
|
|
||||||
xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
|
if (cdf_offset != BAD_CDF_OFFSET) {
|
||||||
xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
|
xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
|
||||||
FIX_ENDIANNESS_CDF(*cdf_ptr);
|
xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
|
||||||
cdf_offset += 4 + CDF_HEADER_LEN
|
FIX_ENDIANNESS_CDF(*cdf_ptr);
|
||||||
+ cdf_ptr->formatted.file_name_length
|
cdf_offset += 4 + CDF_HEADER_LEN
|
||||||
+ cdf_ptr->formatted.extra_field_length
|
+ cdf_ptr->formatted.file_name_length
|
||||||
+ cdf_ptr->formatted.file_comment_length;
|
+ cdf_ptr->formatted.extra_field_length
|
||||||
|
+ cdf_ptr->formatted.file_comment_length;
|
||||||
|
}
|
||||||
|
|
||||||
xlseek(zip_fd, org, SEEK_SET);
|
xlseek(zip_fd, org, SEEK_SET);
|
||||||
return cdf_offset;
|
return cdf_offset;
|
||||||
@ -233,8 +245,9 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
|
|||||||
|
|
||||||
static void unzip_skip(off_t skip)
|
static void unzip_skip(off_t skip)
|
||||||
{
|
{
|
||||||
if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
|
if (skip != 0)
|
||||||
bb_copyfd_exact_size(zip_fd, -1, skip);
|
if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
|
||||||
|
bb_copyfd_exact_size(zip_fd, -1, skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unzip_create_leading_dirs(const char *fn)
|
static void unzip_create_leading_dirs(const char *fn)
|
||||||
@ -535,21 +548,31 @@ int unzip_main(int argc, char **argv)
|
|||||||
bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
|
bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if (cdf_offset != BAD_CDF_OFFSET) {
|
||||||
cdf_header_t cdf_header;
|
cdf_header_t cdf_header;
|
||||||
cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
|
cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
|
||||||
|
/*
|
||||||
|
* Note: cdf_offset can become BAD_CDF_OFFSET after the above call.
|
||||||
|
*/
|
||||||
if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
|
if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
|
||||||
/* 0x0008 - streaming. [u]cmpsize can be reliably gotten
|
/* 0x0008 - streaming. [u]cmpsize can be reliably gotten
|
||||||
* only from Central Directory. See unzip_doc.txt */
|
* only from Central Directory. See unzip_doc.txt
|
||||||
|
*/
|
||||||
zip_header.formatted.crc32 = cdf_header.formatted.crc32;
|
zip_header.formatted.crc32 = cdf_header.formatted.crc32;
|
||||||
zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize;
|
zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize;
|
||||||
zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize;
|
zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize;
|
||||||
}
|
}
|
||||||
if ((cdf_header.formatted.version_made_by >> 8) == 3) {
|
if ((cdf_header.formatted.version_made_by >> 8) == 3) {
|
||||||
/* this archive is created on Unix */
|
/* This archive is created on Unix */
|
||||||
dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
|
dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (cdf_offset == BAD_CDF_OFFSET
|
||||||
|
&& (zip_header.formatted.zip_flags & SWAP_LE16(0x0008))
|
||||||
|
) {
|
||||||
|
/* If it's a streaming zip, we _require_ CDF */
|
||||||
|
bb_error_msg_and_die("can't find file table");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Read filename */
|
/* Read filename */
|
||||||
|
Loading…
Reference in New Issue
Block a user