Add functions in recovery/minzip to libziparchive
Add two functions libziparchive that libminzip has. And create
corresponding unit tests.
1. Open a zip archive from a memory mapped region.
2. A new writer that takes a call back function pointer.
(Used by the OTA updater to stream the data.)
Test: Unit tests passed
Bug: 19472796
Change-Id: I2b2daec71174afe221030357e39bff5faea51e72
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index cc3f0e4..b00557c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -215,19 +215,14 @@
return 0;
}
-static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
- ZipArchive* archive, off64_t file_length,
- off64_t read_amount, uint8_t* scan_buffer) {
+static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
+ off64_t file_length, off64_t read_amount,
+ uint8_t* scan_buffer) {
const off64_t search_start = file_length - read_amount;
- if (lseek64(fd, search_start, SEEK_SET) != search_start) {
- ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
- strerror(errno));
- return kIoError;
- }
- if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
- ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
- strerror(errno));
+ if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
+ ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
+ static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
return kIoError;
}
@@ -287,9 +282,11 @@
* It all looks good. Create a mapping for the CD, and set the fields
* in archive.
*/
- if (!archive->directory_map.create(debug_file_name, fd,
- static_cast<off64_t>(eocd->cd_start_offset),
- static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
+
+ if (!archive->InitializeCentralDirectory(debug_file_name,
+ static_cast<off64_t>(eocd->cd_start_offset),
+ static_cast<size_t>(eocd->cd_size))) {
+ ALOGE("Zip: failed to intialize central directory.\n");
return kMmapFailed;
}
@@ -304,18 +301,16 @@
*
* On success, returns 0 after populating fields from the EOCD area:
* directory_offset
- * directory_map
+ * directory_ptr
* num_entries
*/
-static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
- ZipArchive* archive) {
+static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
// Test file length. We use lseek64 to make sure the file
// is small enough to be a zip file (Its size must be less than
// 0xffffffff bytes).
- off64_t file_length = lseek64(fd, 0, SEEK_END);
+ off64_t file_length = archive->mapped_zip.GetFileLength();
if (file_length == -1) {
- ALOGV("Zip: lseek on fd %d failed", fd);
return kInvalidFile;
}
@@ -346,11 +341,9 @@
read_amount = file_length;
}
- uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
- int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
- file_length, read_amount, scan_buffer);
-
- free(scan_buffer);
+ std::vector<uint8_t> scan_buffer(read_amount);
+ int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
+ scan_buffer.data());
return result;
}
@@ -361,9 +354,8 @@
* Returns 0 on success.
*/
static int32_t ParseZipArchive(ZipArchive* archive) {
- const uint8_t* const cd_ptr =
- reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
- const size_t cd_length = archive->directory_map.getDataLength();
+ const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
+ const size_t cd_length = archive->central_directory.GetMapLength();
const uint16_t num_entries = archive->num_entries;
/*
@@ -437,7 +429,7 @@
static int32_t OpenArchiveInternal(ZipArchive* archive,
const char* debug_file_name) {
int32_t result = -1;
- if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
+ if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
return result;
}
@@ -468,6 +460,13 @@
return OpenArchiveInternal(archive, fileName);
}
+int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
+ ZipArchiveHandle *handle) {
+ ZipArchive* archive = new ZipArchive(address, length);
+ *handle = archive;
+ return OpenArchiveInternal(archive, debug_file_name);
+}
+
/*
* Close a ZipArchive, closing the file and freeing the contents.
*/
@@ -477,10 +476,10 @@
delete archive;
}
-static int32_t UpdateEntryFromDataDescriptor(int fd,
+static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
ZipEntry *entry) {
uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
- if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
+ if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
return kIoError;
}
@@ -495,23 +494,6 @@
return 0;
}
-// Attempts to read |len| bytes into |buf| at offset |off|.
-// On non-Windows platforms, callers are guaranteed that the |fd|
-// offset is unchanged and there is no side effect to this call.
-//
-// On Windows platforms this is not thread-safe.
-static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
-#if !defined(_WIN32)
- return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
-#else
- if (lseek64(fd, off, SEEK_SET) != off) {
- ALOGW("Zip: failed seek to offset %" PRId64, off);
- return false;
- }
- return android::base::ReadFully(fd, buf, len);
-#endif
-}
-
static int32_t FindEntry(const ZipArchive* archive, const int ent,
ZipEntry* data) {
const uint16_t nameLen = archive->hash_table[ent].name_length;
@@ -525,9 +507,8 @@
// This is the base of our mmapped region, we have to sanity check that
// the name that's in the hash table is a pointer to a location within
// this mapped region.
- const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
- archive->directory_map.getDataPtr());
- if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
+ const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
+ if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
ALOGW("Zip: Invalid entry pointer");
return kInvalidOffset;
}
@@ -559,7 +540,7 @@
}
uint8_t lfh_buf[sizeof(LocalFileHeader)];
- if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
+ if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64,
static_cast<int64_t>(local_header_offset));
return kIoError;
@@ -599,19 +580,16 @@
return kInvalidOffset;
}
- uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
- if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
+ std::vector<uint8_t> name_buf(nameLen);
+ if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
- free(name_buf);
return kIoError;
}
- if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
- free(name_buf);
+ if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
return kInconsistentInformation;
}
- free(name_buf);
} else {
ALOGW("Zip: lfh name did not match central directory.");
return kInconsistentInformation;
@@ -881,7 +859,7 @@
}
#pragma GCC diagnostic pop
-static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
+static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
Writer* writer, uint64_t* crc_out) {
const size_t kBufSize = 32768;
std::vector<uint8_t> read_buf(kBufSize);
@@ -931,7 +909,7 @@
/* read as much as we can */
if (zstream.avail_in == 0) {
const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
- if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
+ if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
return kIoError;
}
@@ -979,7 +957,7 @@
return 0;
}
-static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
+static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
uint64_t *crc_out) {
static const uint32_t kBufSize = 32768;
std::vector<uint8_t> buf(kBufSize);
@@ -993,7 +971,7 @@
// Safe conversion because kBufSize is narrow enough for a 32 bit signed
// value.
const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
- if (!android::base::ReadFully(fd, buf.data(), block_size)) {
+ if (!mapped_zip.ReadData(buf.data(), block_size)) {
ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
return kIoError;
}
@@ -1016,7 +994,7 @@
const uint16_t method = entry->method;
off64_t data_offset = entry->offset;
- if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
+ if (!archive->mapped_zip.SeekToOffset(data_offset)) {
ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
return kIoError;
}
@@ -1025,13 +1003,13 @@
int32_t return_value = -1;
uint64_t crc = 0;
if (method == kCompressStored) {
- return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
+ return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
} else if (method == kCompressDeflated) {
- return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
+ return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
}
if (!return_value && entry->has_data_descriptor) {
- return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
+ return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
if (return_value) {
return return_value;
}
@@ -1072,7 +1050,7 @@
}
int GetFileDescriptor(const ZipArchiveHandle handle) {
- return reinterpret_cast<ZipArchive*>(handle)->fd;
+ return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
}
ZipString::ZipString(const char* entry_name)
@@ -1081,3 +1059,143 @@
CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
name_length = static_cast<uint16_t>(len);
}
+
+#if !defined(_WIN32)
+class ProcessWriter : public Writer {
+ public:
+ ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
+ proc_function_(func),
+ cookie_(cookie) {
+ }
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ return proc_function_(buf, buf_size, cookie_);
+ }
+
+ private:
+ ProcessZipEntryFunction proc_function_;
+ void* cookie_;
+};
+
+int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+ ProcessZipEntryFunction func, void* cookie) {
+ ProcessWriter writer(func, cookie);
+ return ExtractToWriter(handle, entry, &writer);
+}
+
+#endif //!defined(_WIN32)
+
+int MappedZipFile::GetFileDescriptor() const {
+ if (!has_fd_) {
+ ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
+ return -1;
+ }
+ return fd_;
+}
+
+void* MappedZipFile::GetBasePtr() const {
+ if (has_fd_) {
+ ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
+ return nullptr;
+ }
+ return base_ptr_;
+}
+
+off64_t MappedZipFile::GetFileLength() const {
+ if (has_fd_) {
+ off64_t result = lseek64(fd_, 0, SEEK_END);
+ if (result == -1) {
+ ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
+ }
+ return result;
+ } else {
+ if (base_ptr_ == nullptr) {
+ ALOGE("Zip: invalid file map\n");
+ return -1;
+ }
+ return static_cast<off64_t>(data_length_);
+ }
+}
+
+bool MappedZipFile::SeekToOffset(off64_t offset) {
+ if (has_fd_) {
+ if (lseek64(fd_, offset, SEEK_SET) != offset) {
+ ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
+ return false;
+ }
+ return true;
+ } else {
+ if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
+ ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
+ data_length_);
+ return false;
+ }
+
+ read_pos_ = offset;
+ return true;
+ }
+}
+
+bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
+ if (has_fd_) {
+ if(!android::base::ReadFully(fd_, buffer, read_amount)) {
+ ALOGE("Zip: read from %d failed\n", fd_);
+ return false;
+ }
+ } else {
+ memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
+ read_pos_ += read_amount;
+ }
+ return true;
+}
+
+// Attempts to read |len| bytes into |buf| at offset |off|.
+bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
+#if !defined(_WIN32)
+ if (has_fd_) {
+ if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
+ ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
+ return false;
+ }
+ return true;
+ }
+#endif
+ if (!SeekToOffset(off)) {
+ return false;
+ }
+ return ReadData(buf, len);
+
+}
+
+void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
+ base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
+ length_ = cd_size;
+}
+
+bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
+ size_t cd_size) {
+ if (mapped_zip.HasFd()) {
+ if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
+ cd_start_offset, cd_size, true /* read only */)) {
+ return false;
+ }
+
+ CHECK_EQ(directory_map->getDataLength(), cd_size);
+ central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
+ } else {
+ if (mapped_zip.GetBasePtr() == nullptr) {
+ ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
+ return false;
+ }
+ if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
+ mapped_zip.GetFileLength()) {
+ ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
+ "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
+ static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
+ return false;
+ }
+
+ central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
+ }
+ return true;
+}