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
This commit is contained in:
parent
047597b3fc
commit
e6884ce56f
|
@ -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 <vector>
|
||||
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
class ZipArchiveStreamEntry {
|
||||
public:
|
||||
virtual ~ZipArchiveStreamEntry() {}
|
||||
|
||||
virtual const std::vector<uint8_t>* 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_
|
|
@ -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)
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -36,11 +36,12 @@
|
|||
#include "log/log.h"
|
||||
#include "utils/Compat.h"
|
||||
#include "utils/FileMap.h"
|
||||
#include "ziparchive/zip_archive.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "entry_name_utils-inl.h"
|
||||
#include "zip_archive_common.h"
|
||||
#include "ziparchive/zip_archive.h"
|
||||
#include "zip_archive_private.h"
|
||||
|
||||
using android::base::get_unaligned;
|
||||
|
||||
|
@ -134,43 +135,6 @@ static const int32_t kErrorMessageLowerBound = -13;
|
|||
* every page that the Central Directory touches. Easier to tuck a copy
|
||||
* of the string length into the hash table entry.
|
||||
*/
|
||||
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 overallocat
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Round up to the next highest power of 2.
|
||||
|
|
|
@ -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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <utils/FileMap.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
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_
|
|
@ -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 <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define LOG_TAG "ZIPARCHIVE"
|
||||
#include <android-base/file.h>
|
||||
#include <log/log.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
#include <ziparchive/zip_archive_stream_entry.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "zip_archive_private.h"
|
||||
|
||||
static constexpr size_t kBufSize = 65535;
|
||||
|
||||
bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
|
||||
ZipArchive* archive = reinterpret_cast<ZipArchive*>(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<uint8_t>* Read() override;
|
||||
|
||||
bool Verify() override;
|
||||
|
||||
protected:
|
||||
bool Init(const ZipEntry& entry) override;
|
||||
|
||||
uint32_t length_;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> 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<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
|
||||
if (length_ == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
|
||||
ZipArchive* archive = reinterpret_cast<ZipArchive*>(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<uint8_t>* Read() override;
|
||||
|
||||
bool Verify() override;
|
||||
|
||||
protected:
|
||||
bool Init(const ZipEntry& entry) override;
|
||||
|
||||
private:
|
||||
bool z_stream_init_ = false;
|
||||
z_stream z_stream_;
|
||||
std::vector<uint8_t> in_;
|
||||
std::vector<uint8_t> 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<uint8_t>* 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<ZipArchive*>(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;
|
||||
}
|
|
@ -14,54 +14,49 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ziparchive/zip_archive.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
#include <ziparchive/zip_archive_stream_entry.h>
|
||||
|
||||
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<uint8_t> 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<uint8_t> kATxtContentsCompressed {
|
||||
'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
|
||||
132, 210, '\\', '\0'
|
||||
};
|
||||
|
||||
static const std::vector<uint8_t> 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<const uint8_t*>(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<uint32_t>(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<ssize_t>(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<ssize_t>(sizeof(kATxtContents)),
|
||||
ASSERT_EQ(data_size + static_cast<ssize_t>(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<uint8_t>* read_data) {
|
||||
ZipString name;
|
||||
SetZipString(&name, entry_name);
|
||||
ASSERT_EQ(0, FindEntry(handle, name, entry));
|
||||
std::unique_ptr<ZipArchiveStreamEntry> 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<uint8_t>* 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<uint8_t>& contents, bool raw) {
|
||||
ZipArchiveHandle handle;
|
||||
ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
|
||||
|
||||
ZipEntry entry;
|
||||
std::vector<uint8_t> 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<uint8_t> read_data;
|
||||
ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
|
||||
|
||||
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<char> 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();
|
||||
|
|
Loading…
Reference in New Issue