/* * 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 "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 ZipFile::OpenAsData() { 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(std::move(file_map)); } else { std::unique_ptr data = std::unique_ptr(new uint8_t[zip_entry_.uncompressed_length]); int32_t result = ExtractToMemory(zip_handle_, &zip_entry_, data.get(), static_cast(zip_entry_.uncompressed_length)); if (result != 0) { return {}; } return util::make_unique(std::move(data), zip_entry_.uncompressed_length); } } std::unique_ptr 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::Create( const StringPiece& path, std::string* out_error) { constexpr static const int32_t kEmptyArchive = -6; std::unique_ptr collection = std::unique_ptr(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, nullptr, nullptr); if (result != 0) { if (out_error) *out_error = ErrorCodeString(result); return {}; } using IterationEnder = std::unique_ptr; IterationEnder iteration_ender(cookie, EndIteration); ZipString zip_entry_name; ZipEntry zip_data; while ((result = Next(cookie, &zip_data, &zip_entry_name)) == 0) { std::string zip_entry_path = std::string(reinterpret_cast(zip_entry_name.name), zip_entry_name.name_length); std::string nested_path = path.to_string() + "@" + zip_entry_path; std::unique_ptr file = util::make_unique(collection->handle_, zip_data, Source(nested_path)); 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 ZipFileCollection::Iterator() { return util::make_unique(this); } ZipFileCollection::~ZipFileCollection() { if (handle_) { CloseArchive(handle_); } } } // namespace io } // namespace aapt