You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
5.0 KiB
175 lines
5.0 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "io/ZipArchive.h"
|
|
|
|
#include "utils/FileMap.h"
|
|
#include "ziparchive/zip_archive.h"
|
|
|
|
#include "Source.h"
|
|
#include "trace/TraceBuffer.h"
|
|
#include "util/Files.h"
|
|
#include "util/Util.h"
|
|
|
|
using ::android::StringPiece;
|
|
|
|
namespace aapt {
|
|
namespace io {
|
|
|
|
ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry,
|
|
const Source& source)
|
|
: zip_handle_(handle), zip_entry_(entry), source_(source) {}
|
|
|
|
std::unique_ptr<IData> ZipFile::OpenAsData() {
|
|
// The file will fail to be mmaped if it is empty
|
|
if (zip_entry_.uncompressed_length == 0) {
|
|
return util::make_unique<EmptyData>();
|
|
}
|
|
|
|
if (zip_entry_.method == kCompressStored) {
|
|
int fd = GetFileDescriptor(zip_handle_);
|
|
|
|
android::FileMap file_map;
|
|
bool result = file_map.create(nullptr, fd, zip_entry_.offset,
|
|
zip_entry_.uncompressed_length, true);
|
|
if (!result) {
|
|
return {};
|
|
}
|
|
return util::make_unique<MmappedData>(std::move(file_map));
|
|
|
|
} else {
|
|
std::unique_ptr<uint8_t[]> data =
|
|
std::unique_ptr<uint8_t[]>(new uint8_t[zip_entry_.uncompressed_length]);
|
|
int32_t result =
|
|
ExtractToMemory(zip_handle_, &zip_entry_, data.get(),
|
|
static_cast<uint32_t>(zip_entry_.uncompressed_length));
|
|
if (result != 0) {
|
|
return {};
|
|
}
|
|
return util::make_unique<MallocData>(std::move(data),
|
|
zip_entry_.uncompressed_length);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<io::InputStream> ZipFile::OpenInputStream() {
|
|
return OpenAsData();
|
|
}
|
|
|
|
const Source& ZipFile::GetSource() const {
|
|
return source_;
|
|
}
|
|
|
|
bool ZipFile::WasCompressed() {
|
|
return zip_entry_.method != kCompressStored;
|
|
}
|
|
|
|
ZipFileCollectionIterator::ZipFileCollectionIterator(
|
|
ZipFileCollection* collection)
|
|
: current_(collection->files_.begin()), end_(collection->files_.end()) {}
|
|
|
|
bool ZipFileCollectionIterator::HasNext() {
|
|
return current_ != end_;
|
|
}
|
|
|
|
IFile* ZipFileCollectionIterator::Next() {
|
|
IFile* result = current_->get();
|
|
++current_;
|
|
return result;
|
|
}
|
|
|
|
ZipFileCollection::ZipFileCollection() : handle_(nullptr) {}
|
|
|
|
std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
|
|
const StringPiece& path, std::string* out_error) {
|
|
TRACE_CALL();
|
|
constexpr static const int32_t kEmptyArchive = -6;
|
|
|
|
std::unique_ptr<ZipFileCollection> collection =
|
|
std::unique_ptr<ZipFileCollection>(new ZipFileCollection());
|
|
|
|
int32_t result = OpenArchive(path.data(), &collection->handle_);
|
|
if (result != 0) {
|
|
// If a zip is empty, result will be an error code. This is fine and we
|
|
// should
|
|
// return an empty ZipFileCollection.
|
|
if (result == kEmptyArchive) {
|
|
return collection;
|
|
}
|
|
|
|
if (out_error) *out_error = ErrorCodeString(result);
|
|
return {};
|
|
}
|
|
|
|
void* cookie = nullptr;
|
|
result = StartIteration(collection->handle_, &cookie);
|
|
if (result != 0) {
|
|
if (out_error) *out_error = ErrorCodeString(result);
|
|
return {};
|
|
}
|
|
|
|
using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
|
|
IterationEnder iteration_ender(cookie, EndIteration);
|
|
|
|
std::string zip_entry_path;
|
|
ZipEntry zip_data;
|
|
while ((result = Next(cookie, &zip_data, &zip_entry_path)) == 0) {
|
|
// Do not add folders to the file collection
|
|
if (util::EndsWith(zip_entry_path, "/")) {
|
|
continue;
|
|
}
|
|
|
|
std::unique_ptr<IFile> file = util::make_unique<ZipFile>(collection->handle_, zip_data,
|
|
Source(zip_entry_path, path.to_string()));
|
|
collection->files_by_name_[zip_entry_path] = file.get();
|
|
collection->files_.push_back(std::move(file));
|
|
}
|
|
|
|
if (result != -1) {
|
|
if (out_error) *out_error = ErrorCodeString(result);
|
|
return {};
|
|
}
|
|
|
|
return collection;
|
|
}
|
|
|
|
IFile* ZipFileCollection::FindFile(const StringPiece& path) {
|
|
auto iter = files_by_name_.find(path.to_string());
|
|
if (iter != files_by_name_.end()) {
|
|
return iter->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<IFileCollectionIterator> ZipFileCollection::Iterator() {
|
|
return util::make_unique<ZipFileCollectionIterator>(this);
|
|
}
|
|
|
|
char ZipFileCollection::GetDirSeparator() {
|
|
// According to the zip file specification, section 4.4.17.1:
|
|
// "All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility
|
|
// with Amiga and UNIX file systems etc."
|
|
return '/';
|
|
}
|
|
|
|
ZipFileCollection::~ZipFileCollection() {
|
|
if (handle_) {
|
|
CloseArchive(handle_);
|
|
}
|
|
}
|
|
|
|
} // namespace io
|
|
} // namespace aapt
|