| /* | |
| * 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 "androidfw/Source.h" | |
| #include "trace/TraceBuffer.h" | |
| #include "util/Files.h" | |
| #include "util/Util.h" | |
| #include "utils/FileMap.h" | |
| #include "ziparchive/zip_archive.h" | |
| using ::android::StringPiece; | |
| namespace aapt { | |
| namespace io { | |
| ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const android::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<android::InputStream> ZipFile::OpenInputStream() { | |
| return OpenAsData(); | |
| } | |
| const android::Source& ZipFile::GetSource() const { | |
| return source_; | |
| } | |
| bool ZipFile::WasCompressed() { | |
| return zip_entry_.method != kCompressStored; | |
| } | |
| bool ZipFile::GetModificationTime(struct tm* buf) const { | |
| if (buf == nullptr) { | |
| return false; | |
| } | |
| *buf = zip_entry_.GetModificationTime(); | |
| return true; | |
| } | |
| 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(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, | |
| android::Source(zip_entry_path, 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(StringPiece path) { | |
| auto iter = files_by_name_.find(path); | |
| 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 |