1 /*
2 * Copyright (C) 2007 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 //
18 // Read-only access to Zip archives, with minimal heap allocation.
19 //
20 #define LOG_TAG "zipro"
21 //#define LOG_NDEBUG 0
22 #include <androidfw/ZipFileRO.h>
23 #include <utils/Log.h>
24 #include <utils/Compat.h>
25 #include <utils/misc.h>
26 #include <utils/threads.h>
27 #include <ziparchive/zip_archive.h>
28
29 #include <zlib.h>
30
31 #include <string.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <unistd.h>
36
37 using namespace android;
38
39 class _ZipEntryRO {
40 public:
41 ZipEntry entry;
42 std::string_view name;
43 void *cookie = nullptr;
44
45 _ZipEntryRO() = default;
46
~_ZipEntryRO()47 ~_ZipEntryRO() {
48 EndIteration(cookie);
49 }
50
convertToPtr()51 android::ZipEntryRO convertToPtr() {
52 _ZipEntryRO* result = new _ZipEntryRO;
53 result->entry = std::move(this->entry);
54 result->name = std::move(this->name);
55 result->cookie = std::exchange(this->cookie, nullptr);
56 return result;
57 }
58
59 private:
60 DISALLOW_COPY_AND_ASSIGN(_ZipEntryRO);
61 };
62
~ZipFileRO()63 ZipFileRO::~ZipFileRO() {
64 CloseArchive(mHandle);
65 if (mFileName != NULL) {
66 free(mFileName);
67 }
68 }
69
70 /*
71 * Open the specified file read-only. We memory-map the entire thing and
72 * close the file before returning.
73 */
open(const char * zipFileName)74 /* static */ ZipFileRO* ZipFileRO::open(const char* zipFileName)
75 {
76 ZipArchiveHandle handle;
77 const int32_t error = OpenArchive(zipFileName, &handle);
78 if (error) {
79 ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error));
80 CloseArchive(handle);
81 return NULL;
82 }
83
84 return new ZipFileRO(handle, strdup(zipFileName));
85 }
86
87
openFd(int fd,const char * debugFileName,bool assume_ownership)88 /* static */ ZipFileRO* ZipFileRO::openFd(int fd, const char* debugFileName,
89 bool assume_ownership)
90 {
91 ZipArchiveHandle handle;
92 const int32_t error = OpenArchiveFd(fd, debugFileName, &handle, assume_ownership);
93 if (error) {
94 ALOGW("Error opening archive fd %d %s: %s", fd, debugFileName, ErrorCodeString(error));
95 CloseArchive(handle);
96 return NULL;
97 }
98
99 return new ZipFileRO(handle, strdup(debugFileName));
100 }
101
findEntryByName(const char * entryName) const102 ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
103 {
104 _ZipEntryRO data;
105 data.name = entryName;
106
107 const int32_t error = FindEntry(mHandle, entryName, &(data.entry));
108 if (error) {
109 return nullptr;
110 }
111
112 return data.convertToPtr();
113 }
114
115 /*
116 * Get the useful fields from the zip entry.
117 *
118 * Returns "false" if the offsets to the fields or the contents of the fields
119 * appear to be bogus.
120 */
getEntryInfo(ZipEntryRO entry,uint16_t * pMethod,uint32_t * pUncompLen,uint32_t * pCompLen,off64_t * pOffset,uint32_t * pModWhen,uint32_t * pCrc32,uint16_t * pExtraFieldSize) const121 bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
122 uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
123 uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const
124 {
125 const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
126 const ZipEntry& ze = zipEntry->entry;
127
128 if (pMethod != nullptr) {
129 *pMethod = ze.method;
130 }
131 if (pUncompLen != nullptr) {
132 *pUncompLen = ze.uncompressed_length;
133 }
134 if (pCompLen != nullptr) {
135 *pCompLen = ze.compressed_length;
136 }
137 if (pOffset != nullptr) {
138 *pOffset = ze.offset;
139 }
140 if (pModWhen != nullptr) {
141 *pModWhen = ze.mod_time;
142 }
143 if (pCrc32 != nullptr) {
144 *pCrc32 = ze.crc32;
145 }
146 if (pExtraFieldSize != nullptr) {
147 *pExtraFieldSize = ze.extra_field_size;
148 }
149
150 return true;
151 }
152
startIteration(void ** cookie)153 bool ZipFileRO::startIteration(void** cookie) {
154 return startIteration(cookie, nullptr, nullptr);
155 }
156
startIteration(void ** cookie,const char * prefix,const char * suffix)157 bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* suffix) {
158 auto result = startIterationOrError(prefix, suffix);
159 if (!result.ok()) {
160 return false;
161 }
162 *cookie = result.value();
163 return true;
164 }
165
166 base::expected<void*, int32_t>
startIterationOrError(const char * prefix,const char * suffix)167 ZipFileRO::startIterationOrError(const char* prefix, const char* suffix) {
168 _ZipEntryRO ze;
169 int32_t error = StartIteration(mHandle, &(ze.cookie),
170 prefix ? prefix : "", suffix ? suffix : "");
171 if (error) {
172 ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
173 ErrorCodeString(error));
174 return base::unexpected(error);
175 }
176
177 return ze.convertToPtr();
178 }
179
nextEntry(void * cookie)180 ZipEntryRO ZipFileRO::nextEntry(void* cookie) {
181 auto result = nextEntryOrError(cookie);
182 if (!result.ok()) {
183 return nullptr;
184 }
185 return result.value();
186 }
187
nextEntryOrError(void * cookie)188 base::expected<ZipEntryRO, int32_t> ZipFileRO::nextEntryOrError(void* cookie) {
189 _ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie);
190 int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name));
191 if (error) {
192 if (error != -1) {
193 ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
194 ErrorCodeString(error));
195 return base::unexpected(error);
196 }
197 return nullptr;
198 }
199
200 return &(ze->entry);
201 }
202
endIteration(void * cookie)203 void ZipFileRO::endIteration(void* cookie)
204 {
205 delete reinterpret_cast<_ZipEntryRO*>(cookie);
206 }
207
releaseEntry(ZipEntryRO entry) const208 void ZipFileRO::releaseEntry(ZipEntryRO entry) const
209 {
210 delete reinterpret_cast<_ZipEntryRO*>(entry);
211 }
212
213 /*
214 * Copy the entry's filename to the buffer.
215 */
getEntryFileName(ZipEntryRO entry,char * buffer,size_t bufLen) const216 int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, size_t bufLen)
217 const
218 {
219 const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
220 const uint16_t requiredSize = zipEntry->name.length() + 1;
221
222 if (bufLen < requiredSize) {
223 ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize);
224 return requiredSize;
225 }
226
227 memcpy(buffer, zipEntry->name.data(), requiredSize - 1);
228 buffer[requiredSize - 1] = '\0';
229
230 return 0;
231 }
232
233 /*
234 * Create a new FileMap object that spans the data in "entry".
235 */
createEntryFileMap(ZipEntryRO entry) const236 FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
237 {
238 const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
239 const ZipEntry& ze = zipEntry->entry;
240 int fd = GetFileDescriptor(mHandle);
241 size_t actualLen = 0;
242
243 if (ze.method == kCompressStored) {
244 actualLen = ze.uncompressed_length;
245 } else {
246 actualLen = ze.compressed_length;
247 }
248
249 FileMap* newMap = new FileMap();
250 if (!newMap->create(mFileName, fd, ze.offset, actualLen, true)) {
251 delete newMap;
252 return NULL;
253 }
254
255 return newMap;
256 }
257
258 /*
259 * Create a new incfs::IncFsFileMap object that spans the data in "entry".
260 */
createEntryIncFsFileMap(ZipEntryRO entry) const261 std::optional<incfs::IncFsFileMap> ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const
262 {
263 const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
264 const ZipEntry& ze = zipEntry->entry;
265 int fd = GetFileDescriptor(mHandle);
266 size_t actualLen = 0;
267
268 if (ze.method == kCompressStored) {
269 actualLen = ze.uncompressed_length;
270 } else {
271 actualLen = ze.compressed_length;
272 }
273
274 incfs::IncFsFileMap newMap;
275 if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) {
276 return std::nullopt;
277 }
278 return std::move(newMap);
279 }
280
281 /*
282 * Uncompress an entry, in its entirety, into the provided output buffer.
283 *
284 * This doesn't verify the data's CRC, which might be useful for
285 * uncompressed data. The caller should be able to manage it.
286 */
uncompressEntry(ZipEntryRO entry,void * buffer,size_t size) const287 bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const
288 {
289 _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
290 const int32_t error = ExtractToMemory(mHandle, &(zipEntry->entry),
291 (uint8_t*) buffer, size);
292 if (error) {
293 ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error));
294 return false;
295 }
296
297 return true;
298 }
299
300 /*
301 * Uncompress an entry, in its entirety, to an open file descriptor.
302 *
303 * This doesn't verify the data's CRC, but probably should.
304 */
uncompressEntry(ZipEntryRO entry,int fd) const305 bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
306 {
307 _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
308 const int32_t error = ExtractEntryToFile(mHandle, &(zipEntry->entry), fd);
309 if (error) {
310 ALOGW("ExtractToFile failed with %s", ErrorCodeString(error));
311 return false;
312 }
313
314 return true;
315 }
316
getZipFileName()317 const char* ZipFileRO::getZipFileName() {
318 return mFileName;
319 }
320