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