summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNarayan Kamath <narayan@google.com>2013-11-28 14:06:24 +0000
committerNarayan Kamath <narayan@google.com>2013-12-09 10:19:24 +0000
commit92572be7f754c213e615a62955cc5f65ca8c0c0e (patch)
tree5d9266c16a5b110bacf1ec6062a46a3e33e16bcb
parent1a7e7d6a885bded1ffcdc8ff2490632698dc5139 (diff)
downloadart-92572be7f754c213e615a62955cc5f65ca8c0c0e.zip
art-92572be7f754c213e615a62955cc5f65ca8c0c0e.tar.gz
art-92572be7f754c213e615a62955cc5f65ca8c0c0e.tar.bz2
Use libziparchive for art zip processing.
This is part of the effort to move all VM & framework zip parsing to a common implementation. This also has the side effect of fixing various TODOs related to crc32 checking. bug: 10193060 Change-Id: I407f9ad5a94fc91d96ff43556adde00a00df1f14
-rw-r--r--dex2oat/dex2oat.cc2
-rw-r--r--runtime/Android.mk6
-rw-r--r--runtime/dex_file.cc8
-rw-r--r--runtime/dex_file_verifier.cc3
-rw-r--r--runtime/native/java_lang_VMClassLoader.cc2
-rw-r--r--runtime/zip_archive.cc524
-rw-r--r--runtime/zip_archive.h87
-rw-r--r--runtime/zip_archive_test.cc4
8 files changed, 66 insertions, 570 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8b23270..28d6649 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -213,7 +213,7 @@ class Dex2Oat {
if (zip_archive.get() == NULL) {
return NULL;
}
- UniquePtr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename));
+ UniquePtr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename, error_msg));
if (zip_entry.get() == NULL) {
*error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
zip_filename, error_msg->c_str());
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 16f11c6..4e5afab 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -344,10 +344,10 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT
LOCAL_SHARED_LIBRARIES += liblog libnativehelper
LOCAL_SHARED_LIBRARIES += libbacktrace # native stack trace support
ifeq ($$(art_target_or_host),target)
- LOCAL_SHARED_LIBRARIES += libcutils libz libdl libselinux
+ LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux
+ LOCAL_STATIC_LIBRARIES := libziparchive libz
else # host
- LOCAL_STATIC_LIBRARIES += libcutils
- LOCAL_SHARED_LIBRARIES += libz-host
+ LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz
LOCAL_LDLIBS += -ldl -lpthread
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 517f96c..463e673 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -98,9 +98,10 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string*
*error_msg = StringPrintf("Failed to open zip archive '%s'", filename);
return false;
}
- UniquePtr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex));
+ UniquePtr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex, error_msg));
if (zip_entry.get() == NULL) {
- *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s", filename, kClassesDex);
+ *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s (error msg: %s)", filename,
+ kClassesDex, error_msg->c_str());
return false;
}
*checksum = zip_entry->GetCrc32();
@@ -240,9 +241,8 @@ const DexFile* DexFile::OpenMemory(const std::string& location,
const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location,
std::string* error_msg) {
CHECK(!location.empty());
- UniquePtr<ZipEntry> zip_entry(zip_archive.Find(kClassesDex));
+ UniquePtr<ZipEntry> zip_entry(zip_archive.Find(kClassesDex, error_msg));
if (zip_entry.get() == NULL) {
- *error_msg = StringPrintf("Failed to find classes.dex within '%s'", location.c_str());
return nullptr;
}
UniquePtr<MemMap> map(zip_entry->ExtractToMemMap(kClassesDex, error_msg));
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 56bf21d..dc9d337 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -16,6 +16,8 @@
#include "dex_file_verifier.h"
+#include <zlib.h>
+
#include "base/stringprintf.h"
#include "dex_file-inl.h"
#include "leb128.h"
@@ -23,7 +25,6 @@
#include "UniquePtr.h"
#include "utf-inl.h"
#include "utils.h"
-#include "zip_archive.h"
namespace art {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index af1b548..314cdb1 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -78,7 +78,7 @@ static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstri
LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg;
return NULL;
}
- UniquePtr<ZipEntry> zip_entry(zip_archive->Find(name.c_str()));
+ UniquePtr<ZipEntry> zip_entry(zip_archive->Find(name.c_str(), &error_msg));
if (zip_entry.get() == NULL) {
return NULL;
}
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index db273ec..8cb1993 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -30,272 +30,23 @@
namespace art {
-static const size_t kBufSize = 32 * KB;
-
-// Get 2 little-endian bytes.
-static uint32_t Le16ToHost(const byte* src) {
- return ((src[0] << 0) |
- (src[1] << 8));
-}
-
-// Get 4 little-endian bytes.
-static uint32_t Le32ToHost(const byte* src) {
- return ((src[0] << 0) |
- (src[1] << 8) |
- (src[2] << 16) |
- (src[3] << 24));
-}
-
-uint16_t ZipEntry::GetCompressionMethod() {
- return Le16ToHost(ptr_ + ZipArchive::kCDEMethod);
-}
-
-uint32_t ZipEntry::GetCompressedLength() {
- return Le32ToHost(ptr_ + ZipArchive::kCDECompLen);
-}
-
uint32_t ZipEntry::GetUncompressedLength() {
- return Le32ToHost(ptr_ + ZipArchive::kCDEUncompLen);
+ return zip_entry_->uncompressed_length;
}
uint32_t ZipEntry::GetCrc32() {
- return Le32ToHost(ptr_ + ZipArchive::kCDECRC);
+ return zip_entry_->crc32;
}
-off64_t ZipEntry::GetDataOffset() {
- // All we have is the offset to the Local File Header, which is
- // variable size, so we have to read the contents of the struct to
- // figure out where the actual data starts.
-
- // We also need to make sure that the lengths are not so large that
- // somebody trying to map the compressed or uncompressed data runs
- // off the end of the mapped region.
-
- off64_t dir_offset = zip_archive_->dir_offset_;
- int64_t lfh_offset = Le32ToHost(ptr_ + ZipArchive::kCDELocalOffset);
- if (lfh_offset + ZipArchive::kLFHLen >= dir_offset) {
- LOG(WARNING) << "Zip: bad LFH offset in zip";
- return -1;
- }
-
- if (lseek64(zip_archive_->fd_, lfh_offset, SEEK_SET) != lfh_offset) {
- PLOG(WARNING) << "Zip: failed seeking to LFH at offset " << lfh_offset;
- return -1;
- }
-
- uint8_t lfh_buf[ZipArchive::kLFHLen];
- ssize_t actual = TEMP_FAILURE_RETRY(read(zip_archive_->fd_, lfh_buf, sizeof(lfh_buf)));
- if (actual != sizeof(lfh_buf)) {
- LOG(WARNING) << "Zip: failed reading LFH from offset " << lfh_offset;
- return -1;
- }
-
- if (Le32ToHost(lfh_buf) != ZipArchive::kLFHSignature) {
- LOG(WARNING) << "Zip: didn't find signature at start of LFH, offset " << lfh_offset;
- return -1;
- }
-
- uint32_t gpbf = Le16ToHost(lfh_buf + ZipArchive::kLFHGPBFlags);
- if ((gpbf & ZipArchive::kGPFUnsupportedMask) != 0) {
- LOG(WARNING) << "Invalid General Purpose Bit Flag: " << gpbf;
- return -1;
- }
-
- off64_t data_offset = (lfh_offset + ZipArchive::kLFHLen
- + Le16ToHost(lfh_buf + ZipArchive::kLFHNameLen)
- + Le16ToHost(lfh_buf + ZipArchive::kLFHExtraLen));
- if (data_offset >= dir_offset) {
- LOG(WARNING) << "Zip: bad data offset " << data_offset << " in zip";
- return -1;
- }
-
- // check lengths
-
- if (static_cast<off64_t>(data_offset + GetCompressedLength()) > dir_offset) {
- LOG(WARNING) << "Zip: bad compressed length in zip "
- << "(" << data_offset << " + " << GetCompressedLength()
- << " > " << dir_offset << ")";
- return -1;
- }
-
- if (GetCompressionMethod() == kCompressStored
- && static_cast<off64_t>(data_offset + GetUncompressedLength()) > dir_offset) {
- LOG(WARNING) << "Zip: bad uncompressed length in zip "
- << "(" << data_offset << " + " << GetUncompressedLength()
- << " > " << dir_offset << ")";
- return -1;
- }
-
- return data_offset;
-}
-
-static bool CopyFdToMemory(uint8_t* begin, size_t size, int in, size_t count) {
- uint8_t* dst = begin;
- std::vector<uint8_t> buf(kBufSize);
- while (count != 0) {
- size_t bytes_to_read = (count > kBufSize) ? kBufSize : count;
- ssize_t actual = TEMP_FAILURE_RETRY(read(in, &buf[0], bytes_to_read));
- if (actual != static_cast<ssize_t>(bytes_to_read)) {
- PLOG(WARNING) << "Zip: short read";
- return false;
- }
- memcpy(dst, &buf[0], bytes_to_read);
- dst += bytes_to_read;
- count -= bytes_to_read;
- }
- DCHECK_EQ(dst, begin + size);
- return true;
-}
-
-class ZStream {
- public:
- ZStream(byte* write_buf, size_t write_buf_size) {
- // Initialize the zlib stream struct.
- memset(&zstream_, 0, sizeof(zstream_));
- zstream_.zalloc = Z_NULL;
- zstream_.zfree = Z_NULL;
- zstream_.opaque = Z_NULL;
- zstream_.next_in = NULL;
- zstream_.avail_in = 0;
- zstream_.next_out = reinterpret_cast<Bytef*>(write_buf);
- zstream_.avail_out = write_buf_size;
- zstream_.data_type = Z_UNKNOWN;
- }
-
- z_stream& Get() {
- return zstream_;
- }
-
- ~ZStream() {
- inflateEnd(&zstream_);
- }
- private:
- z_stream zstream_;
-};
-
-static bool InflateToMemory(uint8_t* begin, size_t size,
- int in, size_t uncompressed_length, size_t compressed_length) {
- uint8_t* dst = begin;
- UniquePtr<uint8_t[]> read_buf(new uint8_t[kBufSize]);
- UniquePtr<uint8_t[]> write_buf(new uint8_t[kBufSize]);
- if (read_buf.get() == NULL || write_buf.get() == NULL) {
- LOG(WARNING) << "Zip: failed to allocate buffer to inflate";
- return false;
- }
-
- UniquePtr<ZStream> zstream(new ZStream(write_buf.get(), kBufSize));
-
- // Use the undocumented "negative window bits" feature to tell zlib
- // that there's no zlib header waiting for it.
- int zerr = inflateInit2(&zstream->Get(), -MAX_WBITS);
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
- } else {
- LOG(WARNING) << "Call to inflateInit2 failed (zerr=" << zerr << ")";
- }
- return false;
- }
-
- size_t remaining = compressed_length;
- do {
- // read as much as we can
- if (zstream->Get().avail_in == 0) {
- size_t bytes_to_read = (remaining > kBufSize) ? kBufSize : remaining;
-
- ssize_t actual = TEMP_FAILURE_RETRY(read(in, read_buf.get(), bytes_to_read));
- if (actual != static_cast<ssize_t>(bytes_to_read)) {
- LOG(WARNING) << "Zip: inflate read failed (" << actual << " vs " << bytes_to_read << ")";
- return false;
- }
- remaining -= bytes_to_read;
- zstream->Get().next_in = read_buf.get();
- zstream->Get().avail_in = bytes_to_read;
- }
-
- // uncompress the data
- zerr = inflate(&zstream->Get(), Z_NO_FLUSH);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- LOG(WARNING) << "Zip: inflate zerr=" << zerr
- << " (next_in=" << zstream->Get().next_in
- << " avail_in=" << zstream->Get().avail_in
- << " next_out=" << zstream->Get().next_out
- << " avail_out=" << zstream->Get().avail_out
- << ")";
- return false;
- }
-
- // write when we're full or when we're done
- if (zstream->Get().avail_out == 0 ||
- (zerr == Z_STREAM_END && zstream->Get().avail_out != kBufSize)) {
- size_t bytes_to_write = zstream->Get().next_out - write_buf.get();
- memcpy(dst, write_buf.get(), bytes_to_write);
- dst += bytes_to_write;
- zstream->Get().next_out = write_buf.get();
- zstream->Get().avail_out = kBufSize;
- }
- } while (zerr == Z_OK);
-
- DCHECK_EQ(zerr, Z_STREAM_END); // other errors should've been caught
-
- // paranoia
- if (zstream->Get().total_out != uncompressed_length) {
- LOG(WARNING) << "Zip: size mismatch on inflated file ("
- << zstream->Get().total_out << " vs " << uncompressed_length << ")";
- return false;
- }
-
- DCHECK_EQ(dst, begin + size);
- return true;
-}
bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) {
- uint32_t length = GetUncompressedLength();
- int result = TEMP_FAILURE_RETRY(ftruncate(file.Fd(), length));
- if (result == -1) {
- *error_msg = StringPrintf("Zip: failed to ftruncate '%s' to length %ud", file.GetPath().c_str(),
- length);
- return false;
- }
-
- UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0,
- file.GetPath().c_str(), error_msg));
- if (map.get() == NULL) {
- *error_msg = StringPrintf("Zip: failed to mmap space for '%s': %s", file.GetPath().c_str(),
- error_msg->c_str());
- return false;
- }
-
- return ExtractToMemory(map->Begin(), map->Size(), error_msg);
-}
-
-bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg) {
- // If size is zero, data offset will be meaningless, so bail out early.
- if (size == 0) {
- return true;
- }
- off64_t data_offset = GetDataOffset();
- if (data_offset == -1) {
- *error_msg = StringPrintf("Zip: data_offset=%lld", data_offset);
- return false;
- }
- if (lseek64(zip_archive_->fd_, data_offset, SEEK_SET) != data_offset) {
- *error_msg = StringPrintf("Zip: lseek to data at %lld failed", data_offset);
+ const int32_t error = ExtractEntryToFile(handle_, zip_entry_, file.Fd());
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
return false;
}
- // TODO: this doesn't verify the data's CRC, but probably should (especially
- // for uncompressed data).
- switch (GetCompressionMethod()) {
- case kCompressStored:
- return CopyFdToMemory(begin, size, zip_archive_->fd_, GetUncompressedLength());
- case kCompressDeflated:
- return InflateToMemory(begin, size, zip_archive_->fd_,
- GetUncompressedLength(), GetCompressedLength());
- default:
- *error_msg = StringPrintf("Zip: unknown compression method 0x%x", GetCompressionMethod());
- return false;
- }
+ return true;
}
MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename, std::string* error_msg) {
@@ -303,18 +54,18 @@ MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename, std::string* error
name += " extracted in memory from ";
name += entry_filename;
UniquePtr<MemMap> map(MemMap::MapAnonymous(name.c_str(),
- NULL,
- GetUncompressedLength(),
+ NULL, GetUncompressedLength(),
PROT_READ | PROT_WRITE, error_msg));
if (map.get() == nullptr) {
DCHECK(!error_msg->empty());
- return NULL;
+ return nullptr;
}
- bool success = ExtractToMemory(map->Begin(), map->Size(), error_msg);
- if (!success) {
- LOG(ERROR) << "Zip: Failed to extract '" << entry_filename << "' to memory";
- return NULL;
+ const int32_t error = ExtractToMemory(handle_, zip_entry_,
+ map->Begin(), map->Size());
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
+ return nullptr;
}
return map.release();
@@ -336,238 +87,47 @@ static void SetCloseOnExec(int fd) {
ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) {
DCHECK(filename != nullptr);
- int fd = open(filename, O_RDONLY, 0);
- if (fd == -1) {
- *error_msg = StringPrintf("Zip: unable to open '%s': %s", filename, strerror(errno));
- return NULL;
- }
- return OpenFromFd(fd, filename, error_msg);
-}
-ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) {
- SetCloseOnExec(fd);
- UniquePtr<ZipArchive> zip_archive(new ZipArchive(fd, filename));
- CHECK(zip_archive.get() != nullptr);
- if (!zip_archive->MapCentralDirectory(error_msg)) {
- zip_archive->Close();
- return NULL;
- }
- if (!zip_archive->Parse(error_msg)) {
- zip_archive->Close();
- return NULL;
+ ZipArchiveHandle handle;
+ const int32_t error = OpenArchive(filename, &handle);
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
+ CloseArchive(handle);
+ return nullptr;
}
- return zip_archive.release();
-}
-
-ZipEntry* ZipArchive::Find(const char* name) const {
- DCHECK(name != NULL);
- DirEntries::const_iterator it = dir_entries_.find(name);
- if (it == dir_entries_.end()) {
- return NULL;
- }
- return new ZipEntry(this, (*it).second);
-}
-void ZipArchive::Close() {
- if (fd_ != -1) {
- close(fd_);
- }
- fd_ = -1;
- num_entries_ = 0;
- dir_offset_ = 0;
-}
-
-std::string ZipArchive::ErrorStringPrintf(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string result(StringPrintf("Zip '%s' : ", filename_.c_str()));
- StringAppendV(&result, fmt, ap);
- va_end(ap);
- return result;
+ SetCloseOnExec(GetFileDescriptor(handle));
+ return new ZipArchive(handle);
}
-// Find the zip Central Directory and memory-map it.
-//
-// On success, returns true after populating fields from the EOCD area:
-// num_entries_
-// dir_offset_
-// dir_map_
-bool ZipArchive::MapCentralDirectory(std::string* error_msg) {
- /*
- * Get and test file length.
- */
- off64_t file_length = lseek64(fd_, 0, SEEK_END);
- if (file_length < kEOCDLen) {
- *error_msg = ErrorStringPrintf("length %lld is too small to be zip", file_length);
- return false;
- }
-
- size_t read_amount = kMaxEOCDSearch;
- if (file_length < off64_t(read_amount)) {
- read_amount = file_length;
- }
-
- UniquePtr<uint8_t[]> scan_buf(new uint8_t[read_amount]);
- CHECK(scan_buf.get() != nullptr);
-
- /*
- * Make sure this is a Zip archive.
- */
- if (lseek64(fd_, 0, SEEK_SET) != 0) {
- *error_msg = ErrorStringPrintf("seek to start failed: %s", strerror(errno));
- return false;
- }
-
- ssize_t actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), sizeof(int32_t)));
- if (actual != static_cast<ssize_t>(sizeof(int32_t))) {
- *error_msg = ErrorStringPrintf("couldn\'t read first signature from zip archive: %s",
- strerror(errno));
- return false;
- }
-
- unsigned int header = Le32ToHost(scan_buf.get());
- if (header != kLFHSignature) {
- *error_msg = ErrorStringPrintf("not a zip archive (found 0x%x)", header);
- return false;
- }
-
- // Perform the traditional EOCD snipe hunt.
- //
- // We're searching for the End of Central Directory magic number,
- // which appears at the start of the EOCD block. It's followed by
- // 18 bytes of EOCD stuff and up to 64KB of archive comment. We
- // need to read the last part of the file into a buffer, dig through
- // it to find the magic number, parse some values out, and use those
- // to determine the extent of the CD.
- //
- // We start by pulling in the last part of the file.
- off64_t search_start = file_length - read_amount;
-
- if (lseek64(fd_, search_start, SEEK_SET) != search_start) {
- *error_msg = ErrorStringPrintf("seek %lld failed: %s", search_start, strerror(errno));
- return false;
- }
- actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), read_amount));
- if (actual != static_cast<ssize_t>(read_amount)) {
- *error_msg = ErrorStringPrintf("read %lld, expected %zd. %s", search_start, read_amount,
- strerror(errno));
- return false;
- }
-
-
- // Scan backward for the EOCD magic. In an archive without a trailing
- // comment, we'll find it on the first try. (We may want to consider
- // doing an initial minimal read; if we don't find it, retry with a
- // second read as above.)
- int i;
- for (i = read_amount - kEOCDLen; i >= 0; i--) {
- if (scan_buf.get()[i] == 0x50 && Le32ToHost(&(scan_buf.get())[i]) == kEOCDSignature) {
- break;
- }
- }
- if (i < 0) {
- *error_msg = ErrorStringPrintf("EOCD not found, not a zip file");
- return false;
- }
-
- off64_t eocd_offset = search_start + i;
- const byte* eocd_ptr = scan_buf.get() + i;
-
- CHECK(eocd_offset < file_length);
-
- // Grab the CD offset and size, and the number of entries in the
- // archive. Verify that they look reasonable.
- uint16_t disk_number = Le16ToHost(eocd_ptr + kEOCDDiskNumber);
- uint16_t disk_with_central_dir = Le16ToHost(eocd_ptr + kEOCDDiskNumberForCD);
- uint16_t num_entries = Le16ToHost(eocd_ptr + kEOCDNumEntries);
- uint16_t total_num_entries = Le16ToHost(eocd_ptr + kEOCDTotalNumEntries);
- uint32_t dir_size = Le32ToHost(eocd_ptr + kEOCDSize);
- uint32_t dir_offset = Le32ToHost(eocd_ptr + kEOCDFileOffset);
- uint16_t comment_size = Le16ToHost(eocd_ptr + kEOCDCommentSize);
-
- if ((uint64_t) dir_offset + (uint64_t) dir_size > (uint64_t) eocd_offset) {
- *error_msg = ErrorStringPrintf("bad offsets (dir=%ud, size=%ud, eocd=%lld)",
- dir_offset, dir_size, eocd_offset);
- return false;
- }
- if (num_entries == 0) {
- *error_msg = ErrorStringPrintf("empty archive?");
- return false;
- } else if (num_entries != total_num_entries || disk_number != 0 || disk_with_central_dir != 0) {
- *error_msg = ErrorStringPrintf("spanned archives not supported");
- return false;
- }
-
- // Check to see if comment is a sane size
- if ((comment_size > (file_length - kEOCDLen))
- || (eocd_offset > (file_length - kEOCDLen) - comment_size)) {
- *error_msg = ErrorStringPrintf("comment size runs off end of file");
- return false;
- }
+ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) {
+ DCHECK(filename != nullptr);
+ DCHECK_GT(fd, 0);
- // It all looks good. Create a mapping for the CD.
- dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset,
- filename_.c_str(), error_msg));
- if (dir_map_.get() == NULL) {
- return false;
+ ZipArchiveHandle handle;
+ const int32_t error = OpenArchiveFd(fd, filename, &handle);
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
+ CloseArchive(handle);
+ return nullptr;
}
- num_entries_ = num_entries;
- dir_offset_ = dir_offset;
- return true;
+ SetCloseOnExec(GetFileDescriptor(handle));
+ return new ZipArchive(handle);
}
-bool ZipArchive::Parse(std::string* error_msg) {
- const byte* cd_ptr = dir_map_->Begin();
- size_t cd_length = dir_map_->Size();
+ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const {
+ DCHECK(name != nullptr);
- // Walk through the central directory, adding entries to the hash
- // table and verifying values.
- const byte* ptr = cd_ptr;
- for (int i = 0; i < num_entries_; i++) {
- if (Le32ToHost(ptr) != kCDESignature) {
- *error_msg = ErrorStringPrintf("missed a central dir sig (at %d)", i);
- return false;
- }
- if (ptr + kCDELen > cd_ptr + cd_length) {
- *error_msg = ErrorStringPrintf("ran off the end (at %d)", i);
- return false;
- }
-
- int64_t local_hdr_offset = Le32ToHost(ptr + kCDELocalOffset);
- if (local_hdr_offset >= dir_offset_) {
- *error_msg = ErrorStringPrintf("bad LFH offset %lld at entry %d", local_hdr_offset, i);
- return false;
- }
-
- uint16_t gpbf = Le16ToHost(ptr + kCDEGPBFlags);
- if ((gpbf & kGPFUnsupportedMask) != 0) {
- *error_msg = ErrorStringPrintf("invalid general purpose bit flag %x", gpbf);
- return false;
- }
-
- uint16_t name_len = Le16ToHost(ptr + kCDENameLen);
- uint16_t extra_len = Le16ToHost(ptr + kCDEExtraLen);
- uint16_t comment_len = Le16ToHost(ptr + kCDECommentLen);
-
- // add the CDE filename to the hash table
- const char* name = reinterpret_cast<const char*>(ptr + kCDELen);
-
- // Check name for NULL characters
- if (memchr(name, 0, name_len) != NULL) {
- *error_msg = ErrorStringPrintf("filename contains NUL byte");
- return false;
- }
-
- dir_entries_.Put(StringPiece(name, name_len), ptr);
- ptr += kCDELen + name_len + extra_len + comment_len;
- if (ptr > cd_ptr + cd_length) {
- *error_msg = ErrorStringPrintf("bad CD advance (%p vs %p) at entry %d",
- ptr, cd_ptr + cd_length, i);
- return false;
- }
+ // Resist the urge to delete the space. <: is a bigraph sequence.
+ UniquePtr< ::ZipEntry> zip_entry(new ::ZipEntry);
+ const int32_t error = FindEntry(handle_, name, zip_entry.get());
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
+ return nullptr;
}
- return true;
+
+ return new ZipEntry(handle_, zip_entry.release());
}
} // namespace art
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index 8ff952b..1f48e0a 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -18,8 +18,8 @@
#define ART_RUNTIME_ZIP_ARCHIVE_H_
#include <stdint.h>
-#include <zlib.h>
#include <string>
+#include <ziparchive/zip_archive.h>
#include "base/logging.h"
#include "base/stringpiece.h"
@@ -38,33 +38,17 @@ class MemMap;
class ZipEntry {
public:
bool ExtractToFile(File& file, std::string* error_msg);
- bool ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg);
MemMap* ExtractToMemMap(const char* entry_filename, std::string* error_msg);
uint32_t GetUncompressedLength();
uint32_t GetCrc32();
private:
- ZipEntry(const ZipArchive* zip_archive, const byte* ptr) : zip_archive_(zip_archive), ptr_(ptr) {}
+ ZipEntry(ZipArchiveHandle handle,
+ ::ZipEntry* zip_entry) : handle_(handle), zip_entry_(zip_entry) {}
- // Zip compression methods
- enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
- };
-
- // kCompressStored, kCompressDeflated, ...
- uint16_t GetCompressionMethod();
-
- uint32_t GetCompressedLength();
-
- // returns -1 on error
- off64_t GetDataOffset();
-
- const ZipArchive* zip_archive_;
-
- // pointer to zip entry within central directory
- const byte* ptr_;
+ ZipArchiveHandle handle_;
+ ::ZipEntry* const zip_entry_;
friend class ZipArchive;
DISALLOW_COPY_AND_ASSIGN(ZipEntry);
@@ -72,74 +56,23 @@ class ZipEntry {
class ZipArchive {
public:
- // Zip file constants.
- static const uint32_t kEOCDSignature = 0x06054b50;
- static const int32_t kEOCDLen = 22;
- static const int32_t kEOCDDiskNumber = 4; // number of the current disk
- static const int32_t kEOCDDiskNumberForCD = 6; // disk number with the Central Directory
- static const int32_t kEOCDNumEntries = 8; // offset to #of entries in file
- static const int32_t kEOCDTotalNumEntries = 10; // offset to total #of entries in spanned archives
- static const int32_t kEOCDSize = 12; // size of the central directory
- static const int32_t kEOCDFileOffset = 16; // offset to central directory
- static const int32_t kEOCDCommentSize = 20; // offset to the length of the file comment
-
- static const int32_t kMaxCommentLen = 65535; // longest possible in uint16_t
- static const int32_t kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen);
-
- static const uint32_t kLFHSignature = 0x04034b50;
- static const int32_t kLFHLen = 30; // excluding variable-len fields
- static const int32_t kLFHGPBFlags = 6; // offset to GPB flags
- static const int32_t kLFHNameLen = 26; // offset to filename length
- static const int32_t kLFHExtraLen = 28; // offset to extra length
-
- static const uint32_t kCDESignature = 0x02014b50;
- static const int32_t kCDELen = 46; // excluding variable-len fields
- static const int32_t kCDEGPBFlags = 8; // offset to GPB flags
- static const int32_t kCDEMethod = 10; // offset to compression method
- static const int32_t kCDEModWhen = 12; // offset to modification timestamp
- static const int32_t kCDECRC = 16; // offset to entry CRC
- static const int32_t kCDECompLen = 20; // offset to compressed length
- static const int32_t kCDEUncompLen = 24; // offset to uncompressed length
- static const int32_t kCDENameLen = 28; // offset to filename length
- static const int32_t kCDEExtraLen = 30; // offset to extra length
- static const int32_t kCDECommentLen = 32; // offset to comment length
- static const int32_t kCDELocalOffset = 42; // offset to local hdr
-
- // General Purpose Bit Flag
- static const int32_t kGPFEncryptedFlag = (1 << 0);
- static const int32_t kGPFUnsupportedMask = (kGPFEncryptedFlag);
-
// return new ZipArchive instance on success, NULL on error.
static ZipArchive* Open(const char* filename, std::string* error_msg);
static ZipArchive* OpenFromFd(int fd, const char* filename, std::string* error_msg);
- ZipEntry* Find(const char* name) const;
+ ZipEntry* Find(const char* name, std::string* error_msg) const;
~ZipArchive() {
- Close();
+ CloseArchive(handle_);
}
private:
- explicit ZipArchive(int fd, const char* filename)
- : fd_(fd), num_entries_(0), dir_offset_(0), filename_(filename) {}
-
- bool MapCentralDirectory(std::string* error_msg);
- bool Parse(std::string* error_msg);
- void Close();
- std::string ErrorStringPrintf(const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
-
- int fd_;
- uint16_t num_entries_;
- off64_t dir_offset_;
- UniquePtr<MemMap> dir_map_;
- typedef SafeMap<StringPiece, const byte*> DirEntries;
- DirEntries dir_entries_;
- // Containing file for error reporting.
- const std::string filename_;
+ explicit ZipArchive(ZipArchiveHandle handle) : handle_(handle) {}
friend class ZipEntry;
+ ZipArchiveHandle handle_;
+
DISALLOW_COPY_AND_ASSIGN(ZipArchive);
};
diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc
index 622dc89..16394b0 100644
--- a/runtime/zip_archive_test.cc
+++ b/runtime/zip_archive_test.cc
@@ -19,6 +19,7 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <zlib.h>
#include "UniquePtr.h"
#include "common_test.h"
@@ -33,8 +34,9 @@ TEST_F(ZipArchiveTest, FindAndExtract) {
UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName().c_str(), &error_msg));
ASSERT_TRUE(zip_archive.get() != false) << error_msg;
ASSERT_TRUE(error_msg.empty());
- UniquePtr<ZipEntry> zip_entry(zip_archive->Find("classes.dex"));
+ UniquePtr<ZipEntry> zip_entry(zip_archive->Find("classes.dex", &error_msg));
ASSERT_TRUE(zip_entry.get() != false);
+ ASSERT_TRUE(error_msg.empty());
ScratchFile tmp;
ASSERT_NE(-1, tmp.GetFd());