From e6884ce56f07933d7292eb0ada1ff752f9c69e7a Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 10 Nov 2015 14:55:12 -0800 Subject: [PATCH] Add a ZipArchiveStreamEntry class. This allows someone to stream the data out of a zip archive instead of extracting to a file or to memory. Included in this change is a small cleanup of the makefile. Change-Id: I8b679a679c3502ff4ea0bc4f9e918303657fa424 --- include/ziparchive/zip_archive_stream_entry.h | 46 +++ libziparchive/Android.mk | 70 ++-- libziparchive/testdata/bad_crc.zip | Bin 0 -> 320 bytes libziparchive/testdata/large.zip | Bin 0 -> 315660 bytes libziparchive/zip_archive.cc | 40 +-- libziparchive/zip_archive_private.h | 63 ++++ libziparchive/zip_archive_stream_entry.cc | 305 ++++++++++++++++++ libziparchive/zip_archive_test.cc | 219 +++++++++---- 8 files changed, 626 insertions(+), 117 deletions(-) create mode 100644 include/ziparchive/zip_archive_stream_entry.h create mode 100644 libziparchive/testdata/bad_crc.zip create mode 100644 libziparchive/testdata/large.zip create mode 100644 libziparchive/zip_archive_private.h create mode 100644 libziparchive/zip_archive_stream_entry.cc diff --git a/include/ziparchive/zip_archive_stream_entry.h b/include/ziparchive/zip_archive_stream_entry.h new file mode 100644 index 000000000..a40b799cf --- /dev/null +++ b/include/ziparchive/zip_archive_stream_entry.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Read-only stream access to Zip archives entries. +#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_ +#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_ + +#include + +#include + +class ZipArchiveStreamEntry { + public: + virtual ~ZipArchiveStreamEntry() {} + + virtual const std::vector* Read() = 0; + + virtual bool Verify() = 0; + + static ZipArchiveStreamEntry* Create(ZipArchiveHandle handle, const ZipEntry& entry); + static ZipArchiveStreamEntry* CreateRaw(ZipArchiveHandle handle, const ZipEntry& entry); + + protected: + ZipArchiveStreamEntry(ZipArchiveHandle handle) : handle_(handle) {} + + virtual bool Init(const ZipEntry& entry); + + ZipArchiveHandle handle_; + + uint32_t crc32_; +}; + +#endif // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_ diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk index 8a4921fc5..056b3e132 100644 --- a/libziparchive/Android.mk +++ b/libziparchive/Android.mk @@ -15,34 +15,46 @@ LOCAL_PATH := $(call my-dir) -source_files := zip_archive.cc zip_writer.cc -test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc +libziparchive_source_files := \ + zip_archive.cc \ + zip_archive_stream_entry.cc \ + zip_writer.cc \ + +libziparchive_test_files := \ + entry_name_utils_test.cc \ + zip_archive_test.cc \ + zip_writer_test.cc \ # ZLIB_CONST turns on const for input buffers, which is pretty standard. -common_c_flags := -Werror -Wall -DZLIB_CONST +libziparchive_common_c_flags := \ + -DZLIB_CONST \ + -Werror \ + -Wall \ # Incorrectly warns when C++11 empty brace {} initializer is used. # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489 -common_cpp_flags := -Wold-style-cast -Wno-missing-field-initializers +libziparchive_common_cpp_flags := \ + -Wold-style-cast \ + -Wno-missing-field-initializers \ include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := ${source_files} +LOCAL_SRC_FILES := $(libziparchive_source_files) LOCAL_STATIC_LIBRARIES := libz LOCAL_SHARED_LIBRARIES := libutils libbase LOCAL_MODULE:= libziparchive -LOCAL_CFLAGS := $(common_c_flags) -LOCAL_CPPFLAGS := $(common_cpp_flags) +LOCAL_CFLAGS := $(libziparchive_common_c_flags) +LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := ${source_files} +LOCAL_SRC_FILES := $(libziparchive_source_files) LOCAL_STATIC_LIBRARIES := libz libutils libbase LOCAL_MODULE:= libziparchive-host -LOCAL_CFLAGS := $(common_c_flags) +LOCAL_CFLAGS := $(libziparchive_common_c_flags) LOCAL_CFLAGS_windows := -mno-ms-bitfields -LOCAL_CPPFLAGS := $(common_cpp_flags) +LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags) LOCAL_MULTILIB := both LOCAL_MODULE_HOST_OS := darwin linux windows @@ -50,12 +62,12 @@ include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := ${source_files} +LOCAL_SRC_FILES := $(libziparchive_source_files) LOCAL_STATIC_LIBRARIES := libutils LOCAL_SHARED_LIBRARIES := libz-host liblog libbase LOCAL_MODULE:= libziparchive-host -LOCAL_CFLAGS := $(common_c_flags) -LOCAL_CPPFLAGS := $(common_cpp_flags) +LOCAL_CFLAGS := $(libziparchive_common_c_flags) +LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags) LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) @@ -63,21 +75,33 @@ include $(BUILD_HOST_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := ziparchive-tests LOCAL_CPP_EXTENSION := .cc -LOCAL_CFLAGS := $(common_c_flags) -LOCAL_CPPFLAGS := $(common_cpp_flags) -LOCAL_SRC_FILES := $(test_files) -LOCAL_SHARED_LIBRARIES := liblog libbase -LOCAL_STATIC_LIBRARIES := libziparchive libz libutils +LOCAL_CFLAGS := $(libziparchive_common_c_flags) +LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags) +LOCAL_SRC_FILES := $(libziparchive_test_files) +LOCAL_SHARED_LIBRARIES := \ + libbase \ + liblog \ + +LOCAL_STATIC_LIBRARIES := \ + libziparchive \ + libz \ + libutils \ + include $(BUILD_NATIVE_TEST) include $(CLEAR_VARS) LOCAL_MODULE := ziparchive-tests-host LOCAL_CPP_EXTENSION := .cc -LOCAL_CFLAGS := $(common_c_flags) -LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags) -LOCAL_SRC_FILES := $(test_files) -LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase +LOCAL_CFLAGS := $(libziparchive_common_c_flags) +LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(libziparchive_common_cpp_flags) +LOCAL_SRC_FILES := $(libziparchive_test_files) +LOCAL_SHARED_LIBRARIES := \ + libziparchive-host \ + liblog \ + libbase \ + LOCAL_STATIC_LIBRARIES := \ + libutils \ libz \ - libutils + include $(BUILD_HOST_NATIVE_TEST) diff --git a/libziparchive/testdata/bad_crc.zip b/libziparchive/testdata/bad_crc.zip new file mode 100644 index 0000000000000000000000000000000000000000..e12ba07bd32cf82061bf5f0095dd4a8b84e62586 GIT binary patch literal 320 zcmWIWW@Zs#U|`^2U~TAfeyYeZl^4hp1Y%YO8HPl?l8Tbh5Kac>POIfXbAY(Cf}4Sn zrPGOlfu%#3fx+9y%g@u-`@BcXr5J_)pjli%oeT_|K(qXI2t{&&*gy<2D+zAaM4&l~ zFwII#N=`{lOV8j6@MdI^W5(qw36KR0j6l4g5yXPJkrm=bH1|P_!sRGrqkMoyL7fOR X3gko#qgdHM_AvqBG$1_%#9;sc>ZUrF literal 0 HcmV?d00001 diff --git a/libziparchive/testdata/large.zip b/libziparchive/testdata/large.zip new file mode 100644 index 0000000000000000000000000000000000000000..49659c832ae598fd5356583f448cbd4ca33f5a42 GIT binary patch literal 315660 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 0) { - close(fd); - } - - free(hash_table); - } -}; /* * Round up to the next highest power of 2. diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h new file mode 100644 index 000000000..ab5236889 --- /dev/null +++ b/libziparchive/zip_archive_private.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_ +#define LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_ + +#include +#include +#include + +#include +#include + +struct ZipArchive { + // open Zip archive + const int fd; + const bool close_file; + + // mapped central directory area + off64_t directory_offset; + android::FileMap directory_map; + + // number of entries in the Zip archive + uint16_t num_entries; + + // We know how many entries are in the Zip archive, so we can have a + // fixed-size hash table. We define a load factor of 0.75 and over + // allocate so the maximum number entries can never be higher than + // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t. + uint32_t hash_table_size; + ZipString* hash_table; + + ZipArchive(const int fd, bool assume_ownership) : + fd(fd), + close_file(assume_ownership), + directory_offset(0), + num_entries(0), + hash_table_size(0), + hash_table(NULL) {} + + ~ZipArchive() { + if (close_file && fd >= 0) { + close(fd); + } + + free(hash_table); + } +}; + +#endif // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_ diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc new file mode 100644 index 000000000..f61883503 --- /dev/null +++ b/libziparchive/zip_archive_stream_entry.cc @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Read-only stream access to Zip Archive entries. +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "ZIPARCHIVE" +#include +#include +#include +#include +#include + +#include "zip_archive_private.h" + +static constexpr size_t kBufSize = 65535; + +bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) { + ZipArchive* archive = reinterpret_cast(handle_); + off64_t data_offset = entry.offset; + if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) { + ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno)); + return false; + } + crc32_ = entry.crc32; + return true; +} + +class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry { + public: + ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {} + virtual ~ZipArchiveStreamEntryUncompressed() {} + + const std::vector* Read() override; + + bool Verify() override; + + protected: + bool Init(const ZipEntry& entry) override; + + uint32_t length_; + + private: + std::vector data_; + uint32_t computed_crc32_; +}; + +bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) { + if (!ZipArchiveStreamEntry::Init(entry)) { + return false; + } + + length_ = entry.uncompressed_length; + + data_.resize(kBufSize); + computed_crc32_ = 0; + + return true; +} + +const std::vector* ZipArchiveStreamEntryUncompressed::Read() { + if (length_ == 0) { + return nullptr; + } + + size_t bytes = (length_ > data_.size()) ? data_.size() : length_; + ZipArchive* archive = reinterpret_cast(handle_); + errno = 0; + if (!android::base::ReadFully(archive->fd, data_.data(), bytes)) { + if (errno != 0) { + ALOGE("Error reading from archive fd: %s", strerror(errno)); + } else { + ALOGE("Short read of zip file, possibly corrupted zip?"); + } + length_ = 0; + return nullptr; + } + + if (bytes < data_.size()) { + data_.resize(bytes); + } + computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size()); + length_ -= bytes; + return &data_; +} + +bool ZipArchiveStreamEntryUncompressed::Verify() { + return length_ == 0 && crc32_ == computed_crc32_; +} + +class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry { + public: + ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {} + virtual ~ZipArchiveStreamEntryCompressed(); + + const std::vector* Read() override; + + bool Verify() override; + + protected: + bool Init(const ZipEntry& entry) override; + + private: + bool z_stream_init_ = false; + z_stream z_stream_; + std::vector in_; + std::vector out_; + uint32_t uncompressed_length_; + uint32_t compressed_length_; + uint32_t computed_crc32_; +}; + +// This method is using libz macros with old-style-casts +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +static inline int zlib_inflateInit2(z_stream* stream, int window_bits) { + return inflateInit2(stream, window_bits); +} +#pragma GCC diagnostic pop + +bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) { + if (!ZipArchiveStreamEntry::Init(entry)) { + return false; + } + + // Initialize the zlib stream struct. + memset(&z_stream_, 0, sizeof(z_stream_)); + z_stream_.zalloc = Z_NULL; + z_stream_.zfree = Z_NULL; + z_stream_.opaque = Z_NULL; + z_stream_.next_in = nullptr; + z_stream_.avail_in = 0; + z_stream_.avail_out = 0; + z_stream_.data_type = Z_UNKNOWN; + + // Use the undocumented "negative window bits" feature to tell zlib + // that there's no zlib header waiting for it. + int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr); + } + + return false; + } + + z_stream_init_ = true; + + uncompressed_length_ = entry.uncompressed_length; + compressed_length_ = entry.compressed_length; + + out_.resize(kBufSize); + in_.resize(kBufSize); + + computed_crc32_ = 0; + + return true; +} + +ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() { + if (z_stream_init_) { + inflateEnd(&z_stream_); + z_stream_init_ = false; + } +} + +bool ZipArchiveStreamEntryCompressed::Verify() { + return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 && + crc32_ == computed_crc32_; +} + +const std::vector* ZipArchiveStreamEntryCompressed::Read() { + if (z_stream_.avail_out == 0) { + z_stream_.next_out = out_.data(); + z_stream_.avail_out = out_.size();; + } + + while (true) { + if (z_stream_.avail_in == 0) { + if (compressed_length_ == 0) { + return nullptr; + } + size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_; + ZipArchive* archive = reinterpret_cast(handle_); + errno = 0; + if (!android::base::ReadFully(archive->fd, in_.data(), bytes)) { + if (errno != 0) { + ALOGE("Error reading from archive fd: %s", strerror(errno)); + } else { + ALOGE("Short read of zip file, possibly corrupted zip?"); + } + return nullptr; + } + + compressed_length_ -= bytes; + z_stream_.next_in = in_.data(); + z_stream_.avail_in = bytes; + } + + int zerr = inflate(&z_stream_, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", + zerr, z_stream_.next_in, z_stream_.avail_in, + z_stream_.next_out, z_stream_.avail_out); + return nullptr; + } + + if (z_stream_.avail_out == 0) { + uncompressed_length_ -= out_.size(); + computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size()); + return &out_; + } + if (zerr == Z_STREAM_END) { + if (z_stream_.avail_out != 0) { + // Resize the vector down to the actual size of the data. + out_.resize(out_.size() - z_stream_.avail_out); + computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size()); + uncompressed_length_ -= out_.size(); + return &out_; + } + return nullptr; + } + } + return nullptr; +} + +class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed { + public: + ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle) + : ZipArchiveStreamEntryUncompressed(handle) {} + virtual ~ZipArchiveStreamEntryRawCompressed() {} + + bool Verify() override; + + protected: + bool Init(const ZipEntry& entry) override; +}; + +bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) { + if (!ZipArchiveStreamEntryUncompressed::Init(entry)) { + return false; + } + length_ = entry.compressed_length; + + return true; +} + +bool ZipArchiveStreamEntryRawCompressed::Verify() { + return length_ == 0; +} + +ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create( + ZipArchiveHandle handle, const ZipEntry& entry) { + ZipArchiveStreamEntry* stream = nullptr; + if (entry.method != kCompressStored) { + stream = new ZipArchiveStreamEntryCompressed(handle); + } else { + stream = new ZipArchiveStreamEntryUncompressed(handle); + } + if (stream && !stream->Init(entry)) { + delete stream; + stream = nullptr; + } + + return stream; +} + +ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw( + ZipArchiveHandle handle, const ZipEntry& entry) { + ZipArchiveStreamEntry* stream = nullptr; + if (entry.method == kCompressStored) { + // Not compressed, don't need to do anything special. + stream = new ZipArchiveStreamEntryUncompressed(handle); + } else { + stream = new ZipArchiveStreamEntryRawCompressed(handle); + } + if (stream && !stream->Init(entry)) { + delete stream; + stream = nullptr; + } + return stream; +} diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc index cb0f41050..d426dc4f4 100644 --- a/libziparchive/zip_archive_test.cc +++ b/libziparchive/zip_archive_test.cc @@ -14,54 +14,49 @@ * limitations under the License. */ -#include "ziparchive/zip_archive.h" - #include #include #include #include +#include #include + #include #include #include +#include +#include static std::string test_data_dir; static const std::string kMissingZip = "missing.zip"; static const std::string kValidZip = "valid.zip"; +static const std::string kLargeZip = "large.zip"; +static const std::string kBadCrcZip = "bad_crc.zip"; -static const uint8_t kATxtContents[] = { +static const std::vector kATxtContents { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n' }; -static const uint8_t kBTxtContents[] = { +static const std::vector kATxtContentsCompressed { + 'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H', + 132, 210, '\\', '\0' +}; + +static const std::vector kBTxtContents { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n' }; -static const uint16_t kATxtNameLength = 5; -static const uint16_t kBTxtNameLength = 5; -static const uint16_t kNonexistentTxtNameLength = 15; -static const uint16_t kEmptyTxtNameLength = 9; - -static const uint8_t kATxtName[kATxtNameLength] = { - 'a', '.', 't', 'x', 't' -}; - -static const uint8_t kBTxtName[kBTxtNameLength] = { - 'b', '.', 't', 'x', 't' -}; - -static const uint8_t kNonexistentTxtName[kNonexistentTxtNameLength] = { - 'n', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 't', 'x' ,'t' -}; - -static const uint8_t kEmptyTxtName[kEmptyTxtNameLength] = { - 'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't' -}; +static const std::string kATxtName("a.txt"); +static const std::string kBTxtName("b.txt"); +static const std::string kNonexistentTxtName("nonexistent.txt"); +static const std::string kEmptyTxtName("empty.txt"); +static const std::string kLargeCompressTxtName("compress.txt"); +static const std::string kLargeUncompressTxtName("uncompress.txt"); static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) { @@ -75,6 +70,11 @@ static void AssertNameEquals(const std::string& name_str, ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length)); } +static void SetZipString(ZipString* zip_str, const std::string& str) { + zip_str->name = reinterpret_cast(str.c_str()); + zip_str->name_length = str.size(); +} + TEST(ziparchive, Open) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); @@ -115,7 +115,7 @@ TEST(ziparchive, Iteration) { ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; - ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL)); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr)); ZipEntry data; ZipString name; @@ -152,7 +152,7 @@ TEST(ziparchive, IterationWithPrefix) { void* iteration_cookie; ZipString prefix("b/"); - ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL)); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, nullptr)); ZipEntry data; ZipString name; @@ -181,7 +181,7 @@ TEST(ziparchive, IterationWithSuffix) { void* iteration_cookie; ZipString suffix(".txt"); - ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix)); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, &suffix)); ZipEntry data; ZipString name; @@ -262,8 +262,7 @@ TEST(ziparchive, FindEntry) { ZipEntry data; ZipString name; - name.name = kATxtName; - name.name_length = kATxtNameLength; + SetZipString(&name, kATxtName); ASSERT_EQ(0, FindEntry(handle, name, &data)); // Known facts about a.txt, from zipinfo -v. @@ -276,8 +275,7 @@ TEST(ziparchive, FindEntry) { // An entry that doesn't exist. Should be a negative return code. ZipString absent_name; - absent_name.name = kNonexistentTxtName; - absent_name.name_length = kNonexistentTxtNameLength; + SetZipString(&absent_name, kNonexistentTxtName); ASSERT_LT(FindEntry(handle, absent_name, &data), 0); CloseArchive(handle); @@ -288,7 +286,7 @@ TEST(ziparchive, TestInvalidDeclaredLength) { ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle)); void* iteration_cookie; - ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL)); + ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr)); ZipString name; ZipEntry data; @@ -306,26 +304,24 @@ TEST(ziparchive, ExtractToMemory) { // An entry that's deflated. ZipEntry data; ZipString a_name; - a_name.name = kATxtName; - a_name.name_length = kATxtNameLength; + SetZipString(&a_name, kATxtName); ASSERT_EQ(0, FindEntry(handle, a_name, &data)); const uint32_t a_size = data.uncompressed_length; - ASSERT_EQ(a_size, sizeof(kATxtContents)); + ASSERT_EQ(a_size, kATxtContents.size()); uint8_t* buffer = new uint8_t[a_size]; ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size)); - ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size)); + ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size)); delete[] buffer; // An entry that's stored. ZipString b_name; - b_name.name = kBTxtName; - b_name.name_length = kBTxtNameLength; + SetZipString(&b_name, kBTxtName); ASSERT_EQ(0, FindEntry(handle, b_name, &data)); const uint32_t b_size = data.uncompressed_length; - ASSERT_EQ(b_size, sizeof(kBTxtContents)); + ASSERT_EQ(b_size, kBTxtContents.size()); buffer = new uint8_t[b_size]; ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size)); - ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size)); + ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size)); delete[] buffer; CloseArchive(handle); @@ -374,8 +370,7 @@ static const uint16_t kAbZip[] = { 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000 }; -static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' }; -static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName); +static const std::string kAbTxtName("ab.txt"); static const size_t kAbUncompressedSize = 270216; static int make_temporary_file(const char* file_name_pattern) { @@ -405,8 +400,7 @@ TEST(ziparchive, EmptyEntries) { ZipEntry entry; ZipString empty_name; - empty_name.name = kEmptyTxtName; - empty_name.name_length = kEmptyTxtNameLength; + SetZipString(&empty_name, kEmptyTxtName); ASSERT_EQ(0, FindEntry(handle, empty_name, &entry)); ASSERT_EQ(static_cast(0), entry.uncompressed_length); uint8_t buffer[1]; @@ -436,8 +430,7 @@ TEST(ziparchive, EntryLargerThan32K) { ZipEntry entry; ZipString ab_name; - ab_name.name = kAbTxtName; - ab_name.name_length = kAbTxtNameLength; + SetZipString(&ab_name, kAbTxtName); ASSERT_EQ(0, FindEntry(handle, ab_name, &entry)); ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length); @@ -504,8 +497,7 @@ TEST(ziparchive, ExtractToFile) { ZipEntry entry; ZipString name; - name.name = kATxtName; - name.name_length = kATxtNameLength; + SetZipString(&name, kATxtName); ASSERT_EQ(0, FindEntry(handle, name, &entry)); ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd)); @@ -521,22 +513,131 @@ TEST(ziparchive, ExtractToFile) { ASSERT_EQ(static_cast(entry.uncompressed_length), TEMP_FAILURE_RETRY( read(fd, &uncompressed_data[0], entry.uncompressed_length))); - ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents, - sizeof(kATxtContents))); + ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), + kATxtContents.size())); // Assert that the total length of the file is sane - ASSERT_EQ(data_size + static_cast(sizeof(kATxtContents)), + ASSERT_EQ(data_size + static_cast(kATxtContents.size()), lseek64(fd, 0, SEEK_END)); close(fd); } +static void ZipArchiveStreamTest( + ZipArchiveHandle& handle, const std::string& entry_name, bool raw, + bool verified, ZipEntry* entry, std::vector* read_data) { + ZipString name; + SetZipString(&name, entry_name); + ASSERT_EQ(0, FindEntry(handle, name, entry)); + std::unique_ptr stream; + if (raw) { + stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry)); + if (entry->method == kCompressStored) { + read_data->resize(entry->uncompressed_length); + } else { + read_data->resize(entry->compressed_length); + } + } else { + stream.reset(ZipArchiveStreamEntry::Create(handle, *entry)); + read_data->resize(entry->uncompressed_length); + } + uint8_t* read_data_ptr = read_data->data(); + ASSERT_TRUE(stream.get() != nullptr); + const std::vector* data; + uint64_t total_size = 0; + while ((data = stream->Read()) != nullptr) { + total_size += data->size(); + memcpy(read_data_ptr, data->data(), data->size()); + read_data_ptr += data->size(); + } + ASSERT_EQ(verified, stream->Verify()); + ASSERT_EQ(total_size, read_data->size()); +} + +static void ZipArchiveStreamTestUsingContents( + const std::string& zip_file, const std::string& entry_name, + const std::vector& contents, bool raw) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle)); + + ZipEntry entry; + std::vector read_data; + ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data); + + ASSERT_EQ(contents.size(), read_data.size()); + ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0); + + CloseArchive(handle); +} + +static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle)); + + ZipEntry entry; + std::vector read_data; + ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data); + + std::vector cmp_data(entry.uncompressed_length); + ASSERT_EQ(entry.uncompressed_length, read_data.size()); + ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size())); + ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0); + + CloseArchive(handle); +} + +TEST(ziparchive, StreamCompressed) { + ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContents, false); +} + +TEST(ziparchive, StreamUncompressed) { + ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, false); +} + +TEST(ziparchive, StreamRawCompressed) { + ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContentsCompressed, true); +} + +TEST(ziparchive, StreamRawUncompressed) { + ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, true); +} + +TEST(ziparchive, StreamLargeCompressed) { + ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeCompressTxtName); +} + +TEST(ziparchive, StreamLargeUncompressed) { + ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeUncompressTxtName); +} + +TEST(ziparchive, StreamCompressedBadCrc) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle)); + + ZipEntry entry; + std::vector read_data; + ZipArchiveStreamTest(handle, kATxtName, false, false, &entry, &read_data); + + CloseArchive(handle); +} + +TEST(ziparchive, StreamUncompressedBadCrc) { + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle)); + + ZipEntry entry; + std::vector read_data; + ZipArchiveStreamTest(handle, kBTxtName, false, false, &entry, &read_data); + + CloseArchive(handle); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); static struct option options[] = { - { "test_data_dir", required_argument, NULL, 't' }, - { NULL, 0, NULL, 0 } + { "test_data_dir", required_argument, nullptr, 't' }, + { nullptr, 0, nullptr, 0 } }; while (true) { @@ -557,9 +658,15 @@ int main(int argc, char** argv) { } if (test_data_dir[0] != '/') { - printf("Test data must be an absolute path, was %s\n\n", - test_data_dir.c_str()); - return -2; + std::vector cwd_buffer(1024); + const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1); + if (cwd == nullptr) { + printf("Cannot get current working directory, use an absolute path instead, was %s\n\n", + test_data_dir.c_str()); + return -2; + } + test_data_dir = '/' + test_data_dir; + test_data_dir = cwd + test_data_dir; } return RUN_ALL_TESTS();