summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/unzip.cpp
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2016-04-11 15:36:13 +1000
committer Vas Crabb <vas@vastheman.com>2016-04-11 15:47:57 +1000
commitefffa8a6f38f44d789004b452fe57c83d5b587f2 (patch)
tree80559885874f76881dde62e5ef42921a41bcced4 /src/lib/util/unzip.cpp
parentd818313f8f75089ccf3bbf2697577ded9097c899 (diff)
Make unzip less opaque about what it's doing
Diffstat (limited to 'src/lib/util/unzip.cpp')
-rw-r--r--src/lib/util/unzip.cpp269
1 files changed, 246 insertions, 23 deletions
diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp
index 181a192d002..ad51400d9d1 100644
--- a/src/lib/util/unzip.cpp
+++ b/src/lib/util/unzip.cpp
@@ -63,6 +63,7 @@ public:
{
ptr result;
std::swap(s_cache[cachenum], result);
+ osd_printf_verbose("unzip: found %s in cache\n", filename.c_str());
return result;
}
}
@@ -84,23 +85,51 @@ public:
return ziperr;
// verify that we can work with this zipfile (no disk spanning allowed)
- if ((m_ecd.disk_number != m_ecd.cd_start_disk_number) ||
- (m_ecd.cd_disk_entries != m_ecd.cd_total_entries) ||
- (std::size_t(m_ecd.cd_size) != m_ecd.cd_size))
+ if (m_ecd.disk_number != m_ecd.cd_start_disk_number)
+ {
+ osd_printf_error("unzip: %s central directory starts in another segment\n", m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (m_ecd.cd_disk_entries != m_ecd.cd_total_entries)
+ {
+ osd_printf_error("unzip: %s not all central directory entries reside in this segment\n", m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
+ if (std::size_t(m_ecd.cd_size) != m_ecd.cd_size)
+ {
+ osd_printf_error("unzip: %s central directory too large to load\n", m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
// allocate memory for the central directory
try { m_cd.resize(std::size_t(m_ecd.cd_size)); }
- catch (...) { return archive_file::error::OUT_OF_MEMORY; }
+ catch (...)
+ {
+ osd_printf_error("unzip: %s failed to allocate memory for central directory\n", m_filename.c_str());
+ return archive_file::error::OUT_OF_MEMORY;
+ }
// read the central directory
- // FIXME: central directory can theoretically be bigger than maximum 32-bit integer value, should use a loop
- std::uint32_t read_length;
- auto const filerr = m_file->read(&m_cd[0], m_ecd.cd_start_disk_offset, m_ecd.cd_size, read_length);
- if (filerr != osd_file::error::NONE)
- return archive_file::error::FILE_ERROR;
- if (read_length != m_ecd.cd_size)
- return archive_file::error::FILE_TRUNCATED;
+ auto cd_remaining(m_ecd.cd_size);
+ std::size_t cd_offs(0);
+ while (cd_remaining)
+ {
+ std::uint32_t const chunk(std::uint32_t((std::min<std::uint64_t>)(std::numeric_limits<std::uint32_t>::max(), cd_remaining)));
+ std::uint32_t read_length(0);
+ auto const filerr = m_file->read(&m_cd[cd_offs], m_ecd.cd_start_disk_offset + cd_offs, chunk, read_length);
+ if (filerr != osd_file::error::NONE)
+ {
+ osd_printf_error("unzip: %s error reading central directory (%d)\n", m_filename.c_str(), int(filerr));
+ return archive_file::error::FILE_ERROR;
+ }
+ if (!read_length)
+ {
+ osd_printf_error("unzip: %s unexpectedly reached end-of-file while reading central directory\n", m_filename.c_str());
+ return archive_file::error::FILE_TRUNCATED;
+ }
+ cd_remaining -= read_length;
+ cd_offs += read_length;
+ }
return archive_file::error::NONE;
}
@@ -152,7 +181,15 @@ private:
{
auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, m_file, m_length);
if (filerr != osd_file::error::NONE)
+ {
+ // this would spam every time it looks for a non-existent archive, which is a lot
+ //osd_printf_error("unzip: error reopening archive file %s (%d)\n", m_filename.c_str(), int(filerr));
return archive_file::error::FILE_ERROR;
+ }
+ else
+ {
+ osd_printf_verbose("unzip: opened archive file %s\n", m_filename.c_str());
+ }
}
return archive_file::error::NONE;
}
@@ -555,7 +592,11 @@ void zip_file_impl::close(ptr &&zip)
// if no room left in the cache, free the bottommost entry
if (cachenum == s_cache.size())
- s_cache[--cachenum].reset();
+ {
+ cachenum--;
+ osd_printf_verbose("unzip: removing %s from cache to make space\n", s_cache[cachenum]->m_filename.c_str());
+ s_cache[cachenum].reset();
+ }
// move everyone else down and place us at the top
for ( ; cachenum > 0; cachenum--)
@@ -672,11 +713,17 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length
// if we don't have enough buffer, error
if (length < m_header.uncompressed_length)
+ {
+ osd_printf_error("unzip: buffer too small to decompress %s from %s\n", m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::BUFFER_TOO_SMALL;
+ }
// make sure the info in the header aligns with what we know
if (m_header.start_disk_number != m_ecd.disk_number)
+ {
+ osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
// get the compressed data offset
ziperr = get_compressed_data_offset(offset);
@@ -699,6 +746,9 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length
break;
default:
+ osd_printf_error(
+ "unzip: %s in %s uses unsupported compression method %u\n",
+ m_header.file_name.c_str(), m_filename.c_str(), m_header.compression);
ziperr = archive_file::error::UNSUPPORTED;
break;
}
@@ -735,12 +785,19 @@ archive_file::error zip_file_impl::read_ecd()
// allocate buffer
std::unique_ptr<std::uint8_t []> buffer;
try { buffer.reset(new std::uint8_t[buflen + 1]); }
- catch (...) { return archive_file::error::OUT_OF_MEMORY; }
+ catch (...)
+ {
+ osd_printf_error("unzip: %s failed to allocate memory for ECD search\n", m_filename.c_str());
+ return archive_file::error::OUT_OF_MEMORY;
+ }
// read in one buffers' worth of data
error = m_file->read(&buffer[0], m_length - buflen, buflen, read_length);
if ((error != osd_file::error::NONE) || (read_length != buflen))
+ {
+ osd_printf_error("unzip: %s error reading file to search for ECD (%d)\n", m_filename.c_str(), int(error));
return archive_file::error::FILE_ERROR;
+ }
// find the ECD signature
std::int32_t offset;
@@ -765,7 +822,10 @@ archive_file::error zip_file_impl::read_ecd()
// is the file too small to contain a ZIP64 ECD locator?
if ((m_length - buflen + offset) < ecd64_locator_reader::minimum_length())
+ {
+ osd_printf_verbose("unzip: %s too small to contain ZIP64 ECD locator\n", m_filename.c_str());
return archive_file::error::NONE;
+ }
// try to read the ZIP64 ECD locator
error = m_file->read(
@@ -774,32 +834,61 @@ archive_file::error zip_file_impl::read_ecd()
ecd64_locator_reader::minimum_length(),
read_length);
if ((error != osd_file::error::NONE) || (read_length != ecd64_locator_reader::minimum_length()))
+ {
+ osd_printf_error("unzip: error reading %s to search for ZIP64 ECD locator (%d)\n", m_filename.c_str(), int(error));
return archive_file::error::FILE_ERROR;
+ }
// if the signature isn't correct, it's not a ZIP64 archive
ecd64_locator_reader const ecd64_loc_rd(buffer.get());
if (!ecd64_loc_rd.signature_correct())
+ {
+ osd_printf_verbose("unzip: %s has no ZIP64 ECD locator\n", m_filename.c_str());
return archive_file::error::NONE;
+ }
// check that the ZIP64 ECD is in this segment (assuming this segment is the last segment)
if ((ecd64_loc_rd.ecd64_disk() + 1) != ecd64_loc_rd.total_disks())
+ {
+ osd_printf_error("unzip: %s ZIP64 ECD resides in a different segment\n", m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
// try to read the ZIP64 ECD
error = m_file->read(&buffer[0], ecd64_loc_rd.ecd64_offset(), ecd64_reader::minimum_length(), read_length);
if (error != osd_file::error::NONE)
+ {
+ osd_printf_error("unzip: error reading %s ZIP64 ECD (%d)\n", m_filename.c_str(), int(error));
return archive_file::error::FILE_ERROR;
+ }
if (read_length != ecd64_reader::minimum_length())
+ {
+ osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s ZIP64 ECD\n", m_filename.c_str());
return archive_file::error::FILE_TRUNCATED;
+ }
// check ZIP64 ECD
ecd64_reader const ecd64_rd(buffer.get());
if (!ecd64_rd.signature_correct())
+ {
+ osd_printf_error(
+ "unzip: %s ZIP64 ECD has incorrect signature 0x%08lx\n",
+ m_filename.c_str(), long(ecd64_rd.signature()));
return archive_file::error::BAD_SIGNATURE;
+ }
if (ecd64_rd.total_length() < ecd64_reader::minimum_length())
+ {
+ osd_printf_error("unzip: %s ZIP64 ECD appears to be too small\n", m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
if (ecd64_rd.version_needed() > 63)
+ {
+ osd_printf_error(
+ "unzip: %s ZIP64 ECD requires unsupported version %u.%u\n",
+ m_filename.c_str(), unsigned(ecd64_rd.version_needed() / 10), unsigned(ecd64_rd.version_needed() % 10));
return archive_file::error::UNSUPPORTED;
+ }
+ osd_printf_verbose("unzip: found %s ZIP64 ECD\n", m_filename.c_str());
// extract ZIP64 ECD info
m_ecd.disk_number = ecd64_rd.this_disk_no();
@@ -814,10 +903,16 @@ archive_file::error zip_file_impl::read_ecd()
// didn't find it; free this buffer and expand our search
if (buflen < m_length)
+ {
buflen *= 2;
+ }
else
+ {
+ osd_printf_error("unzip: %s couldn't find ECD\n", m_filename.c_str());
return archive_file::error::BAD_SIGNATURE;
+ }
}
+ osd_printf_error("unzip: %s couldn't find ECD in last 64KiB\n", m_filename.c_str());
return archive_file::error::OUT_OF_MEMORY;
}
@@ -831,12 +926,28 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off
{
// don't support a number of features
general_flag_reader const flags(m_header.bit_flag);
- if ((m_header.start_disk_number != m_ecd.disk_number) ||
- (m_header.version_needed > 63) ||
- flags.encrypted() ||
- flags.patch_data() ||
- flags.strong_encryption())
+ if (m_header.start_disk_number != m_ecd.disk_number)
+ {
+ osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
+ if (m_header.version_needed > 63)
+ {
+ osd_printf_error(
+ "unzip: %s in %s requires unsupported version %u.%u\n",
+ m_header.file_name.c_str(), m_filename.c_str(), unsigned(m_header.version_needed / 10), unsigned(m_header.version_needed % 10));
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (flags.encrypted() || flags.strong_encryption())
+ {
+ osd_printf_error("unzip: %s in %s is encrypted\n", m_header.file_name.c_str(), m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (flags.patch_data())
+ {
+ osd_printf_error("unzip: %s in %s is compressed patch data\n", m_header.file_name.c_str(), m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
// make sure the file handle is open
auto const ziperr = reopen();
@@ -847,14 +958,29 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off
std::uint32_t read_length;
auto const error = m_file->read(&m_buffer[0], m_header.local_header_offset, local_file_header_reader::minimum_length(), read_length);
if (error != osd_file::error::NONE)
+ {
+ osd_printf_error(
+ "unzip: error reading local file header for %s in %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(error));
return archive_file::error::FILE_ERROR;
+ }
if (read_length != local_file_header_reader::minimum_length())
+ {
+ osd_printf_error(
+ "unzip: unexpectedly reached end-of-file while reading local file header for %s in %s\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::FILE_TRUNCATED;
+ }
// compute the final offset
local_file_header_reader reader(&m_buffer[0]);
if (!reader.signature_correct())
+ {
+ osd_printf_error(
+ "unzip: local file header for %s in %s has incorrect signature %04lx\n",
+ m_header.file_name.c_str(), m_filename.c_str(), long(reader.signature()));
return archive_file::error::BAD_SIGNATURE;
+ }
offset = m_header.local_header_offset + reader.total_length();
return archive_file::error::NONE;
@@ -873,16 +999,23 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off
archive_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length)
{
- std::uint32_t read_length;
-
// the data is uncompressed; just read it
+ std::uint32_t read_length(0);
auto const filerr = m_file->read(buffer, offset, m_header.compressed_length, read_length);
if (filerr != osd_file::error::NONE)
+ {
+ osd_printf_error("unzip: error reading %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
return archive_file::error::FILE_ERROR;
+ }
else if (read_length != m_header.compressed_length)
+ {
+ osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s from %s\n", m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::FILE_TRUNCATED;
+ }
else
+ {
return archive_file::error::NONE;
+ }
}
@@ -908,13 +1041,18 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
// initialize the decompressor
zerr = inflateInit2(&stream, -MAX_WBITS);
if (zerr != Z_OK)
+ {
+ osd_printf_error(
+ "unzip: error allocating zlib stream to inflate %s from %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), zerr);
return archive_file::error::DECOMPRESS_ERROR;
+ }
// loop until we're done
while (1)
{
// read in the next chunk of data
- std::uint32_t read_length;
+ std::uint32_t read_length(0);
auto const filerr = m_file->read(
&m_buffer[0],
offset,
@@ -922,6 +1060,9 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
read_length);
if (filerr != osd_file::error::NONE)
{
+ osd_printf_error(
+ "unzip: error reading compressed data for %s in %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
inflateEnd(&stream);
return archive_file::error::FILE_ERROR;
}
@@ -930,6 +1071,9 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
// if we read nothing, but still have data left, the file is truncated
if ((read_length == 0) && (input_remaining > 0))
{
+ osd_printf_error(
+ "unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n",
+ m_header.file_name.c_str(), m_filename.c_str());
inflateEnd(&stream);
return archive_file::error::FILE_TRUNCATED;
}
@@ -946,9 +1090,12 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
// now inflate
zerr = inflate(&stream, Z_NO_FLUSH);
if (zerr == Z_STREAM_END)
+ {
break;
- if (zerr != Z_OK)
+ }
+ else if (zerr != Z_OK)
{
+ osd_printf_error("unzip: error inflating %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), zerr);
inflateEnd(&stream);
return archive_file::error::DECOMPRESS_ERROR;
}
@@ -957,11 +1104,19 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
// finish decompression
zerr = inflateEnd(&stream);
if (zerr != Z_OK)
+ {
+ osd_printf_error("unzip: error finishing inflation of %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), zerr);
return archive_file::error::DECOMPRESS_ERROR;
+ }
// if anything looks funny, report an error
if ((stream.avail_out > 0) || (input_remaining > 0))
+ {
+ osd_printf_error(
+ "unzip: inflation of %s from %s doesn't appear to have completed correctly\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::DECOMPRESS_ERROR;
+ }
return archive_file::error::NONE;
}
@@ -1001,33 +1156,78 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
// need to read LZMA properties before we can initialise the decompressor
if (4 > input_remaining)
+ {
+ osd_printf_error(
+ "unzip:compressed data for %s in %s is too small to hold LZMA properties header\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::DECOMPRESS_ERROR;
+ }
filerr = m_file->read(&m_buffer[0], offset, 4, read_length);
if (filerr != osd_file::error::NONE)
+ {
+ osd_printf_error(
+ "unzip: error reading LZMA properties header for %s in %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
return archive_file::error::FILE_ERROR;
+ }
offset += read_length;
input_remaining -= read_length;
if (4 != read_length)
+ {
+ osd_printf_error(
+ "unzip: unexpectedly reached end-of-file while reading LZMA properties header for %s in %s\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::FILE_TRUNCATED;
+ }
std::uint16_t const props_size((std::uint16_t(m_buffer[3]) << 8) | std::uint16_t(m_buffer[2]));
if (props_size > m_buffer.size())
+ {
+ osd_printf_error(
+ "unzip: %s in %s has excessively large LZMA properties\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
else if (props_size > input_remaining)
+ {
+ osd_printf_error(
+ "unzip:compressed data for %s in %s is too small to hold LZMA properties\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::DECOMPRESS_ERROR;
+ }
filerr = m_file->read(&m_buffer[0], offset, props_size, read_length);
if (filerr != osd_file::error::NONE)
+ {
+ osd_printf_error(
+ "unzip: error reading LZMA properties for %s in %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
return archive_file::error::FILE_ERROR;
+ }
offset += read_length;
input_remaining -= read_length;
if (props_size != read_length)
+ {
+ osd_printf_error(
+ "unzip: unexpectedly reached end-of-file while reading LZMA properties for %s in %s\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::FILE_TRUNCATED;
+ }
// initialize the decompressor
lzerr = LzmaDec_Allocate(&stream, &m_buffer[0], props_size, &alloc_imp);
if (SZ_ERROR_MEM == lzerr)
+ {
+ osd_printf_error(
+ "unzip: memory error allocating LZMA decoder to decompress %s from %s\n",
+ m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::OUT_OF_MEMORY;
+ }
else if (SZ_OK != lzerr)
+ {
+ osd_printf_error(
+ "unzip: error allocating LZMA decoder to decompress %s from %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(lzerr));
return archive_file::error::DECOMPRESS_ERROR;
+ }
LzmaDec_Init(&stream);
// loop until we're done
@@ -1041,6 +1241,9 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
read_length);
if (filerr != osd_file::error::NONE)
{
+ osd_printf_error(
+ "unzip: error reading compressed data for %s in %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
LzmaDec_Free(&stream, &alloc_imp);
return archive_file::error::FILE_ERROR;
}
@@ -1050,6 +1253,9 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
// if we read nothing, but still have data left, the file is truncated
if ((0 == read_length) && (0 < input_remaining))
{
+ osd_printf_error(
+ "unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n",
+ m_header.file_name.c_str(), m_filename.c_str());
LzmaDec_Free(&stream, &alloc_imp);
return archive_file::error::FILE_TRUNCATED;
}
@@ -1068,6 +1274,7 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
&lzstatus);
if (SZ_OK != lzerr)
{
+ osd_printf_error("unzip: error decoding LZMA data for %s in %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), int(lzerr));
LzmaDec_Free(&stream, &alloc_imp);
return archive_file::error::DECOMPRESS_ERROR;
}
@@ -1078,11 +1285,27 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
// if anything looks funny, report an error
if (LZMA_STATUS_FINISHED_WITH_MARK == lzstatus)
+ {
return archive_file::error::NONE;
- else if ((LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK != lzstatus) || eos_mark)
+ }
+ else if (eos_mark)
+ {
+ osd_printf_error(
+ "unzip: LZMA end mark not found for %s in %s (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(lzstatus));
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ else if (LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK != lzstatus)
+ {
+ osd_printf_error(
+ "unzip: LZMA decompression of %s from %s doesn't appear to have completed correctly (%d)\n",
+ m_header.file_name.c_str(), m_filename.c_str(), int(lzstatus));
return archive_file::error::DECOMPRESS_ERROR;
+ }
else
+ {
return archive_file::error::NONE;
+ }
}
} // anonymous namespace