1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "io/ZipArchive.h"
18 
19 #include "utils/FileMap.h"
20 #include "ziparchive/zip_archive.h"
21 
22 #include "Source.h"
23 #include "util/Util.h"
24 
25 using android::StringPiece;
26 
27 namespace aapt {
28 namespace io {
29 
ZipFile(ZipArchiveHandle handle,const ZipEntry & entry,const Source & source)30 ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry,
31                  const Source& source)
32     : zip_handle_(handle), zip_entry_(entry), source_(source) {}
33 
OpenAsData()34 std::unique_ptr<IData> ZipFile::OpenAsData() {
35   if (zip_entry_.method == kCompressStored) {
36     int fd = GetFileDescriptor(zip_handle_);
37 
38     android::FileMap file_map;
39     bool result = file_map.create(nullptr, fd, zip_entry_.offset,
40                                   zip_entry_.uncompressed_length, true);
41     if (!result) {
42       return {};
43     }
44     return util::make_unique<MmappedData>(std::move(file_map));
45 
46   } else {
47     std::unique_ptr<uint8_t[]> data =
48         std::unique_ptr<uint8_t[]>(new uint8_t[zip_entry_.uncompressed_length]);
49     int32_t result =
50         ExtractToMemory(zip_handle_, &zip_entry_, data.get(),
51                         static_cast<uint32_t>(zip_entry_.uncompressed_length));
52     if (result != 0) {
53       return {};
54     }
55     return util::make_unique<MallocData>(std::move(data),
56                                          zip_entry_.uncompressed_length);
57   }
58 }
59 
GetSource() const60 const Source& ZipFile::GetSource() const { return source_; }
61 
WasCompressed()62 bool ZipFile::WasCompressed() {
63   return zip_entry_.method != kCompressStored;
64 }
65 
ZipFileCollectionIterator(ZipFileCollection * collection)66 ZipFileCollectionIterator::ZipFileCollectionIterator(
67     ZipFileCollection* collection)
68     : current_(collection->files_.begin()), end_(collection->files_.end()) {}
69 
HasNext()70 bool ZipFileCollectionIterator::HasNext() { return current_ != end_; }
71 
Next()72 IFile* ZipFileCollectionIterator::Next() {
73   IFile* result = current_->get();
74   ++current_;
75   return result;
76 }
77 
ZipFileCollection()78 ZipFileCollection::ZipFileCollection() : handle_(nullptr) {}
79 
Create(const StringPiece & path,std::string * out_error)80 std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
81     const StringPiece& path, std::string* out_error) {
82   constexpr static const int32_t kEmptyArchive = -6;
83 
84   std::unique_ptr<ZipFileCollection> collection =
85       std::unique_ptr<ZipFileCollection>(new ZipFileCollection());
86 
87   int32_t result = OpenArchive(path.data(), &collection->handle_);
88   if (result != 0) {
89     // If a zip is empty, result will be an error code. This is fine and we
90     // should
91     // return an empty ZipFileCollection.
92     if (result == kEmptyArchive) {
93       return collection;
94     }
95 
96     if (out_error) *out_error = ErrorCodeString(result);
97     return {};
98   }
99 
100   void* cookie = nullptr;
101   result = StartIteration(collection->handle_, &cookie, nullptr, nullptr);
102   if (result != 0) {
103     if (out_error) *out_error = ErrorCodeString(result);
104     return {};
105   }
106 
107   using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
108   IterationEnder iteration_ender(cookie, EndIteration);
109 
110   ZipString zip_entry_name;
111   ZipEntry zip_data;
112   while ((result = Next(cookie, &zip_data, &zip_entry_name)) == 0) {
113     std::string zip_entry_path =
114         std::string(reinterpret_cast<const char*>(zip_entry_name.name),
115                     zip_entry_name.name_length);
116     std::string nested_path = path.to_string() + "@" + zip_entry_path;
117     std::unique_ptr<IFile> file =
118         util::make_unique<ZipFile>(collection->handle_, zip_data, Source(nested_path));
119     collection->files_by_name_[zip_entry_path] = file.get();
120     collection->files_.push_back(std::move(file));
121   }
122 
123   if (result != -1) {
124     if (out_error) *out_error = ErrorCodeString(result);
125     return {};
126   }
127   return collection;
128 }
129 
FindFile(const StringPiece & path)130 IFile* ZipFileCollection::FindFile(const StringPiece& path) {
131   auto iter = files_by_name_.find(path.to_string());
132   if (iter != files_by_name_.end()) {
133     return iter->second;
134   }
135   return nullptr;
136 }
137 
Iterator()138 std::unique_ptr<IFileCollectionIterator> ZipFileCollection::Iterator() {
139   return util::make_unique<ZipFileCollectionIterator>(this);
140 }
141 
~ZipFileCollection()142 ZipFileCollection::~ZipFileCollection() {
143   if (handle_) {
144     CloseArchive(handle_);
145   }
146 }
147 
148 }  // namespace io
149 }  // namespace aapt
150