xref: /aosp_15_r20/frameworks/base/tools/aapt2/io/ZipArchive.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "io/ZipArchive.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include "androidfw/Source.h"
20*d57664e9SAndroid Build Coastguard Worker #include "trace/TraceBuffer.h"
21*d57664e9SAndroid Build Coastguard Worker #include "util/Files.h"
22*d57664e9SAndroid Build Coastguard Worker #include "util/Util.h"
23*d57664e9SAndroid Build Coastguard Worker #include "utils/FileMap.h"
24*d57664e9SAndroid Build Coastguard Worker #include "ziparchive/zip_archive.h"
25*d57664e9SAndroid Build Coastguard Worker 
26*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece;
27*d57664e9SAndroid Build Coastguard Worker 
28*d57664e9SAndroid Build Coastguard Worker namespace aapt {
29*d57664e9SAndroid Build Coastguard Worker namespace io {
30*d57664e9SAndroid Build Coastguard Worker 
ZipFile(ZipArchiveHandle handle,const ZipEntry & entry,const android::Source & source)31*d57664e9SAndroid Build Coastguard Worker ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const android::Source& source)
32*d57664e9SAndroid Build Coastguard Worker     : zip_handle_(handle), zip_entry_(entry), source_(source) {
33*d57664e9SAndroid Build Coastguard Worker }
34*d57664e9SAndroid Build Coastguard Worker 
OpenAsData()35*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<IData> ZipFile::OpenAsData() {
36*d57664e9SAndroid Build Coastguard Worker   // The file will fail to be mmaped if it is empty
37*d57664e9SAndroid Build Coastguard Worker   if (zip_entry_.uncompressed_length == 0) {
38*d57664e9SAndroid Build Coastguard Worker     return util::make_unique<EmptyData>();
39*d57664e9SAndroid Build Coastguard Worker   }
40*d57664e9SAndroid Build Coastguard Worker 
41*d57664e9SAndroid Build Coastguard Worker   if (zip_entry_.method == kCompressStored) {
42*d57664e9SAndroid Build Coastguard Worker     int fd = GetFileDescriptor(zip_handle_);
43*d57664e9SAndroid Build Coastguard Worker 
44*d57664e9SAndroid Build Coastguard Worker     android::FileMap file_map;
45*d57664e9SAndroid Build Coastguard Worker     bool result = file_map.create(nullptr, fd, zip_entry_.offset,
46*d57664e9SAndroid Build Coastguard Worker                                   zip_entry_.uncompressed_length, true);
47*d57664e9SAndroid Build Coastguard Worker     if (!result) {
48*d57664e9SAndroid Build Coastguard Worker       return {};
49*d57664e9SAndroid Build Coastguard Worker     }
50*d57664e9SAndroid Build Coastguard Worker     return util::make_unique<MmappedData>(std::move(file_map));
51*d57664e9SAndroid Build Coastguard Worker 
52*d57664e9SAndroid Build Coastguard Worker   } else {
53*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<uint8_t[]> data =
54*d57664e9SAndroid Build Coastguard Worker         std::unique_ptr<uint8_t[]>(new uint8_t[zip_entry_.uncompressed_length]);
55*d57664e9SAndroid Build Coastguard Worker     int32_t result =
56*d57664e9SAndroid Build Coastguard Worker         ExtractToMemory(zip_handle_, &zip_entry_, data.get(),
57*d57664e9SAndroid Build Coastguard Worker                         static_cast<uint32_t>(zip_entry_.uncompressed_length));
58*d57664e9SAndroid Build Coastguard Worker     if (result != 0) {
59*d57664e9SAndroid Build Coastguard Worker       return {};
60*d57664e9SAndroid Build Coastguard Worker     }
61*d57664e9SAndroid Build Coastguard Worker     return util::make_unique<MallocData>(std::move(data),
62*d57664e9SAndroid Build Coastguard Worker                                          zip_entry_.uncompressed_length);
63*d57664e9SAndroid Build Coastguard Worker   }
64*d57664e9SAndroid Build Coastguard Worker }
65*d57664e9SAndroid Build Coastguard Worker 
OpenInputStream()66*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::InputStream> ZipFile::OpenInputStream() {
67*d57664e9SAndroid Build Coastguard Worker   return OpenAsData();
68*d57664e9SAndroid Build Coastguard Worker }
69*d57664e9SAndroid Build Coastguard Worker 
GetSource() const70*d57664e9SAndroid Build Coastguard Worker const android::Source& ZipFile::GetSource() const {
71*d57664e9SAndroid Build Coastguard Worker   return source_;
72*d57664e9SAndroid Build Coastguard Worker }
73*d57664e9SAndroid Build Coastguard Worker 
WasCompressed()74*d57664e9SAndroid Build Coastguard Worker bool ZipFile::WasCompressed() {
75*d57664e9SAndroid Build Coastguard Worker   return zip_entry_.method != kCompressStored;
76*d57664e9SAndroid Build Coastguard Worker }
77*d57664e9SAndroid Build Coastguard Worker 
GetModificationTime(struct tm * buf) const78*d57664e9SAndroid Build Coastguard Worker bool ZipFile::GetModificationTime(struct tm* buf) const {
79*d57664e9SAndroid Build Coastguard Worker   if (buf == nullptr) {
80*d57664e9SAndroid Build Coastguard Worker     return false;
81*d57664e9SAndroid Build Coastguard Worker   }
82*d57664e9SAndroid Build Coastguard Worker   *buf = zip_entry_.GetModificationTime();
83*d57664e9SAndroid Build Coastguard Worker   return true;
84*d57664e9SAndroid Build Coastguard Worker }
85*d57664e9SAndroid Build Coastguard Worker 
ZipFileCollectionIterator(ZipFileCollection * collection)86*d57664e9SAndroid Build Coastguard Worker ZipFileCollectionIterator::ZipFileCollectionIterator(
87*d57664e9SAndroid Build Coastguard Worker     ZipFileCollection* collection)
88*d57664e9SAndroid Build Coastguard Worker     : current_(collection->files_.begin()), end_(collection->files_.end()) {}
89*d57664e9SAndroid Build Coastguard Worker 
HasNext()90*d57664e9SAndroid Build Coastguard Worker bool ZipFileCollectionIterator::HasNext() {
91*d57664e9SAndroid Build Coastguard Worker   return current_ != end_;
92*d57664e9SAndroid Build Coastguard Worker }
93*d57664e9SAndroid Build Coastguard Worker 
Next()94*d57664e9SAndroid Build Coastguard Worker IFile* ZipFileCollectionIterator::Next() {
95*d57664e9SAndroid Build Coastguard Worker   IFile* result = current_->get();
96*d57664e9SAndroid Build Coastguard Worker   ++current_;
97*d57664e9SAndroid Build Coastguard Worker   return result;
98*d57664e9SAndroid Build Coastguard Worker }
99*d57664e9SAndroid Build Coastguard Worker 
ZipFileCollection()100*d57664e9SAndroid Build Coastguard Worker ZipFileCollection::ZipFileCollection() : handle_(nullptr) {}
101*d57664e9SAndroid Build Coastguard Worker 
Create(StringPiece path,std::string * out_error)102*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(StringPiece path,
103*d57664e9SAndroid Build Coastguard Worker                                                              std::string* out_error) {
104*d57664e9SAndroid Build Coastguard Worker   TRACE_CALL();
105*d57664e9SAndroid Build Coastguard Worker   constexpr static const int32_t kEmptyArchive = -6;
106*d57664e9SAndroid Build Coastguard Worker 
107*d57664e9SAndroid Build Coastguard Worker   std::unique_ptr<ZipFileCollection> collection =
108*d57664e9SAndroid Build Coastguard Worker       std::unique_ptr<ZipFileCollection>(new ZipFileCollection());
109*d57664e9SAndroid Build Coastguard Worker 
110*d57664e9SAndroid Build Coastguard Worker   int32_t result = OpenArchive(path.data(), &collection->handle_);
111*d57664e9SAndroid Build Coastguard Worker   if (result != 0) {
112*d57664e9SAndroid Build Coastguard Worker     // If a zip is empty, result will be an error code. This is fine and we
113*d57664e9SAndroid Build Coastguard Worker     // should
114*d57664e9SAndroid Build Coastguard Worker     // return an empty ZipFileCollection.
115*d57664e9SAndroid Build Coastguard Worker     if (result == kEmptyArchive) {
116*d57664e9SAndroid Build Coastguard Worker       return collection;
117*d57664e9SAndroid Build Coastguard Worker     }
118*d57664e9SAndroid Build Coastguard Worker 
119*d57664e9SAndroid Build Coastguard Worker     if (out_error) *out_error = ErrorCodeString(result);
120*d57664e9SAndroid Build Coastguard Worker     return {};
121*d57664e9SAndroid Build Coastguard Worker   }
122*d57664e9SAndroid Build Coastguard Worker 
123*d57664e9SAndroid Build Coastguard Worker   void* cookie = nullptr;
124*d57664e9SAndroid Build Coastguard Worker   result = StartIteration(collection->handle_, &cookie);
125*d57664e9SAndroid Build Coastguard Worker   if (result != 0) {
126*d57664e9SAndroid Build Coastguard Worker     if (out_error) *out_error = ErrorCodeString(result);
127*d57664e9SAndroid Build Coastguard Worker     return {};
128*d57664e9SAndroid Build Coastguard Worker   }
129*d57664e9SAndroid Build Coastguard Worker 
130*d57664e9SAndroid Build Coastguard Worker   using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
131*d57664e9SAndroid Build Coastguard Worker   IterationEnder iteration_ender(cookie, EndIteration);
132*d57664e9SAndroid Build Coastguard Worker 
133*d57664e9SAndroid Build Coastguard Worker   std::string zip_entry_path;
134*d57664e9SAndroid Build Coastguard Worker   ZipEntry zip_data;
135*d57664e9SAndroid Build Coastguard Worker   while ((result = Next(cookie, &zip_data, &zip_entry_path)) == 0) {
136*d57664e9SAndroid Build Coastguard Worker     // Do not add folders to the file collection
137*d57664e9SAndroid Build Coastguard Worker     if (util::EndsWith(zip_entry_path, "/")) {
138*d57664e9SAndroid Build Coastguard Worker       continue;
139*d57664e9SAndroid Build Coastguard Worker     }
140*d57664e9SAndroid Build Coastguard Worker 
141*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<IFile> file = util::make_unique<ZipFile>(collection->handle_, zip_data,
142*d57664e9SAndroid Build Coastguard Worker                                                              android::Source(zip_entry_path, path));
143*d57664e9SAndroid Build Coastguard Worker     collection->files_by_name_[zip_entry_path] = file.get();
144*d57664e9SAndroid Build Coastguard Worker     collection->files_.push_back(std::move(file));
145*d57664e9SAndroid Build Coastguard Worker   }
146*d57664e9SAndroid Build Coastguard Worker 
147*d57664e9SAndroid Build Coastguard Worker   if (result != -1) {
148*d57664e9SAndroid Build Coastguard Worker     if (out_error) *out_error = ErrorCodeString(result);
149*d57664e9SAndroid Build Coastguard Worker     return {};
150*d57664e9SAndroid Build Coastguard Worker   }
151*d57664e9SAndroid Build Coastguard Worker 
152*d57664e9SAndroid Build Coastguard Worker   return collection;
153*d57664e9SAndroid Build Coastguard Worker }
154*d57664e9SAndroid Build Coastguard Worker 
FindFile(StringPiece path)155*d57664e9SAndroid Build Coastguard Worker IFile* ZipFileCollection::FindFile(StringPiece path) {
156*d57664e9SAndroid Build Coastguard Worker   auto iter = files_by_name_.find(path);
157*d57664e9SAndroid Build Coastguard Worker   if (iter != files_by_name_.end()) {
158*d57664e9SAndroid Build Coastguard Worker     return iter->second;
159*d57664e9SAndroid Build Coastguard Worker   }
160*d57664e9SAndroid Build Coastguard Worker   return nullptr;
161*d57664e9SAndroid Build Coastguard Worker }
162*d57664e9SAndroid Build Coastguard Worker 
Iterator()163*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<IFileCollectionIterator> ZipFileCollection::Iterator() {
164*d57664e9SAndroid Build Coastguard Worker   return util::make_unique<ZipFileCollectionIterator>(this);
165*d57664e9SAndroid Build Coastguard Worker }
166*d57664e9SAndroid Build Coastguard Worker 
GetDirSeparator()167*d57664e9SAndroid Build Coastguard Worker char ZipFileCollection::GetDirSeparator() {
168*d57664e9SAndroid Build Coastguard Worker   // According to the zip file specification, section  4.4.17.1:
169*d57664e9SAndroid Build Coastguard Worker   // "All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility
170*d57664e9SAndroid Build Coastguard Worker   // with Amiga and UNIX file systems etc."
171*d57664e9SAndroid Build Coastguard Worker   return '/';
172*d57664e9SAndroid Build Coastguard Worker }
173*d57664e9SAndroid Build Coastguard Worker 
~ZipFileCollection()174*d57664e9SAndroid Build Coastguard Worker ZipFileCollection::~ZipFileCollection() {
175*d57664e9SAndroid Build Coastguard Worker   if (handle_) {
176*d57664e9SAndroid Build Coastguard Worker     CloseArchive(handle_);
177*d57664e9SAndroid Build Coastguard Worker   }
178*d57664e9SAndroid Build Coastguard Worker }
179*d57664e9SAndroid Build Coastguard Worker 
180*d57664e9SAndroid Build Coastguard Worker }  // namespace io
181*d57664e9SAndroid Build Coastguard Worker }  // namespace aapt
182