diff options
author | Narayan Kamath <narayan@google.com> | 2013-11-28 14:06:24 +0000 |
---|---|---|
committer | Narayan Kamath <narayan@google.com> | 2013-12-09 10:19:24 +0000 |
commit | 92572be7f754c213e615a62955cc5f65ca8c0c0e (patch) | |
tree | 5d9266c16a5b110bacf1ec6062a46a3e33e16bcb | |
parent | 1a7e7d6a885bded1ffcdc8ff2490632698dc5139 (diff) | |
download | art-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.cc | 2 | ||||
-rw-r--r-- | runtime/Android.mk | 6 | ||||
-rw-r--r-- | runtime/dex_file.cc | 8 | ||||
-rw-r--r-- | runtime/dex_file_verifier.cc | 3 | ||||
-rw-r--r-- | runtime/native/java_lang_VMClassLoader.cc | 2 | ||||
-rw-r--r-- | runtime/zip_archive.cc | 524 | ||||
-rw-r--r-- | runtime/zip_archive.h | 87 | ||||
-rw-r--r-- | runtime/zip_archive_test.cc | 4 |
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()); |