blob: 42cc349e8fb962d3deda5deeb7c5b18911a32fad [file] [log] [blame]
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001/*
2 * Copyright (C) 2006 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// Access to Zip archives.
19//
20
21#define LOG_TAG "zip"
22
Mathias Agopian3344b2e2009-06-05 14:55:48 -070023#include <utils/Log.h>
Narayan Kamath0e4110e2017-10-26 18:00:13 +010024#include <ziparchive/zip_archive.h>
Mathias Agopian3344b2e2009-06-05 14:55:48 -070025
26#include "ZipFile.h"
27
28#include <zlib.h>
Mathias Agopian3344b2e2009-06-05 14:55:48 -070029
Raph Levien093d04c2014-07-07 16:00:29 -070030#include "zopfli/deflate.h"
31
Mathias Agopian3344b2e2009-06-05 14:55:48 -070032#include <memory.h>
33#include <sys/stat.h>
34#include <errno.h>
35#include <assert.h>
Dan Willemsen41bc4242015-11-04 14:08:20 -080036#include <inttypes.h>
Mathias Agopian3344b2e2009-06-05 14:55:48 -070037
Elliott Hughes04434732023-01-10 22:59:40 +000038_Static_assert(sizeof(off_t) == 8, "off_t too small");
39
Fabien Sanglard0f29f542020-10-22 17:58:12 -070040namespace android {
Mathias Agopian3344b2e2009-06-05 14:55:48 -070041
42/*
43 * Some environments require the "b", some choke on it.
44 */
45#define FILE_OPEN_RO "rb"
46#define FILE_OPEN_RW "r+b"
47#define FILE_OPEN_RW_CREATE "w+b"
48
49/* should live somewhere else? */
50static status_t errnoToStatus(int err)
51{
52 if (err == ENOENT)
53 return NAME_NOT_FOUND;
54 else if (err == EACCES)
55 return PERMISSION_DENIED;
56 else
57 return UNKNOWN_ERROR;
58}
59
60/*
61 * Open a file and parse its guts.
62 */
63status_t ZipFile::open(const char* zipFileName, int flags)
64{
65 bool newArchive = false;
66
67 assert(mZipFp == NULL); // no reopen
68
69 if ((flags & kOpenTruncate))
70 flags |= kOpenCreate; // trunc implies create
71
72 if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
73 return INVALID_OPERATION; // not both
74 if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
75 return INVALID_OPERATION; // not neither
76 if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
77 return INVALID_OPERATION; // create requires write
78
79 if (flags & kOpenTruncate) {
80 newArchive = true;
81 } else {
82 newArchive = (access(zipFileName, F_OK) != 0);
83 if (!(flags & kOpenCreate) && newArchive) {
84 /* not creating, must already exist */
Steve Block15fab1a2011-12-20 16:25:43 +000085 ALOGD("File %s does not exist", zipFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -070086 return NAME_NOT_FOUND;
87 }
88 }
89
90 /* open the file */
91 const char* openflags;
92 if (flags & kOpenReadWrite) {
93 if (newArchive)
94 openflags = FILE_OPEN_RW_CREATE;
95 else
96 openflags = FILE_OPEN_RW;
97 } else {
98 openflags = FILE_OPEN_RO;
99 }
100 mZipFp = fopen(zipFileName, openflags);
101 if (mZipFp == NULL) {
102 int err = errno;
Steve Block15fab1a2011-12-20 16:25:43 +0000103 ALOGD("fopen failed: %d\n", err);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700104 return errnoToStatus(err);
105 }
106
107 status_t result;
108 if (!newArchive) {
109 /*
110 * Load the central directory. If that fails, then this probably
111 * isn't a Zip archive.
112 */
113 result = readCentralDir();
114 } else {
115 /*
116 * Newly-created. The EndOfCentralDir constructor actually
117 * sets everything to be the way we want it (all zeroes). We
118 * set mNeedCDRewrite so that we create *something* if the
119 * caller doesn't add any files. (We could also just unlink
120 * the file if it's brand new and nothing was added, but that's
121 * probably doing more than we really should -- the user might
122 * have a need for empty zip files.)
123 */
124 mNeedCDRewrite = true;
Elliott Hughesad7d5622018-10-08 11:19:28 -0700125 result = OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700126 }
127
128 if (flags & kOpenReadOnly)
129 mReadOnly = true;
130 else
131 assert(!mReadOnly);
132
133 return result;
134}
135
136/*
137 * Return the Nth entry in the archive.
138 */
Fabien Sanglard0f29f542020-10-22 17:58:12 -0700139ZipEntry* ZipFile::getEntryByIndex(int idx) const
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700140{
141 if (idx < 0 || idx >= (int) mEntries.size())
142 return NULL;
143
144 return mEntries[idx];
145}
146
147/*
148 * Find an entry by name.
149 */
Fabien Sanglard0f29f542020-10-22 17:58:12 -0700150ZipEntry* ZipFile::getEntryByName(const char* fileName) const
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700151{
152 /*
153 * Do a stupid linear string-compare search.
154 *
155 * There are various ways to speed this up, especially since it's rare
156 * to intermingle changes to the archive with "get by name" calls. We
157 * don't want to sort the mEntries vector itself, however, because
158 * it's used to recreate the Central Directory.
159 *
160 * (Hash table works, parallel list of pointers in sorted order is good.)
161 */
162 int idx;
163
164 for (idx = mEntries.size()-1; idx >= 0; idx--) {
165 ZipEntry* pEntry = mEntries[idx];
166 if (!pEntry->getDeleted() &&
167 strcmp(fileName, pEntry->getFileName()) == 0)
168 {
169 return pEntry;
170 }
171 }
172
173 return NULL;
174}
175
176/*
177 * Empty the mEntries vector.
178 */
179void ZipFile::discardEntries(void)
180{
181 int count = mEntries.size();
182
183 while (--count >= 0)
184 delete mEntries[count];
185
186 mEntries.clear();
187}
188
189
190/*
191 * Find the central directory and read the contents.
192 *
193 * The fun thing about ZIP archives is that they may or may not be
194 * readable from start to end. In some cases, notably for archives
195 * that were written to stdout, the only length information is in the
196 * central directory at the end of the file.
197 *
198 * Of course, the central directory can be followed by a variable-length
199 * comment field, so we have to scan through it backwards. The comment
200 * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
201 * itself, plus apparently sometimes people throw random junk on the end
202 * just for the fun of it.
203 *
204 * This is all a little wobbly. If the wrong value ends up in the EOCD
205 * area, we're hosed. This appears to be the way that everbody handles
206 * it though, so we're in pretty good company if this fails.
207 */
208status_t ZipFile::readCentralDir(void)
209{
Elliott Hughes04434732023-01-10 22:59:40 +0000210 fseeko(mZipFp, 0, SEEK_END);
211 off_t fileLength = ftello(mZipFp);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700212 rewind(mZipFp);
213
214 /* too small to be a ZIP archive? */
215 if (fileLength < EndOfCentralDir::kEOCDLen) {
Elliott Hughes04434732023-01-10 22:59:40 +0000216 ALOGD("Length is %lld -- too small\n", (long long) fileLength);
217 return INVALID_OPERATION;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700218 }
219
Elliott Hughes04434732023-01-10 22:59:40 +0000220 off_t seekStart;
221 size_t readAmount;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700222 if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
223 seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
224 readAmount = EndOfCentralDir::kMaxEOCDSearch;
225 } else {
226 seekStart = 0;
Elliott Hughes04434732023-01-10 22:59:40 +0000227 readAmount = fileLength;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700228 }
Elliott Hughes04434732023-01-10 22:59:40 +0000229 if (fseeko(mZipFp, seekStart, SEEK_SET) != 0) {
230 ALOGD("Failure seeking to end of zip at %lld", (long long) seekStart);
231 return UNKNOWN_ERROR;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700232 }
233
234 /* read the last part of the file into the buffer */
Elliott Hughes04434732023-01-10 22:59:40 +0000235 uint8_t buf[EndOfCentralDir::kMaxEOCDSearch];
236 if (fread(buf, 1, readAmount, mZipFp) != readAmount) {
Colin Cross383e4822021-01-08 11:06:24 -0800237 if (feof(mZipFp)) {
Elliott Hughes04434732023-01-10 22:59:40 +0000238 ALOGW("fread %zu bytes failed, unexpected EOF", readAmount);
Colin Cross383e4822021-01-08 11:06:24 -0800239 } else {
Elliott Hughes04434732023-01-10 22:59:40 +0000240 ALOGW("fread %zu bytes failed, %s", readAmount, strerror(errno));
Colin Cross383e4822021-01-08 11:06:24 -0800241 }
Elliott Hughes04434732023-01-10 22:59:40 +0000242 return UNKNOWN_ERROR;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700243 }
244
245 /* find the end-of-central-dir magic */
Elliott Hughes04434732023-01-10 22:59:40 +0000246 int i;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700247 for (i = readAmount - 4; i >= 0; i--) {
248 if (buf[i] == 0x50 &&
249 ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
250 {
Steve Block2da72c62011-10-20 11:56:20 +0100251 ALOGV("+++ Found EOCD at buf+%d\n", i);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700252 break;
253 }
254 }
255 if (i < 0) {
Steve Block15fab1a2011-12-20 16:25:43 +0000256 ALOGD("EOCD not found, not Zip\n");
Elliott Hughes04434732023-01-10 22:59:40 +0000257 return INVALID_OPERATION;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700258 }
259
260 /* extract eocd values */
Elliott Hughes04434732023-01-10 22:59:40 +0000261 status_t result = mEOCD.readBuf(buf + i, readAmount - i);
Elliott Hughesad7d5622018-10-08 11:19:28 -0700262 if (result != OK) {
Elliott Hughes04434732023-01-10 22:59:40 +0000263 ALOGD("Failure reading %zu bytes of EOCD values", readAmount - i);
264 return result;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700265 }
266 //mEOCD.dump();
267
268 if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
269 mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
270 {
Steve Block15fab1a2011-12-20 16:25:43 +0000271 ALOGD("Archive spanning not supported\n");
Elliott Hughes04434732023-01-10 22:59:40 +0000272 return INVALID_OPERATION;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700273 }
274
275 /*
276 * So far so good. "mCentralDirSize" is the size in bytes of the
277 * central directory, so we can just seek back that far to find it.
278 * We can also seek forward mCentralDirOffset bytes from the
279 * start of the file.
280 *
281 * We're not guaranteed to have the rest of the central dir in the
282 * buffer, nor are we guaranteed that the central dir will have any
283 * sort of convenient size. We need to skip to the start of it and
284 * read the header, then the other goodies.
285 *
286 * The only thing we really need right now is the file comment, which
287 * we're hoping to preserve.
288 */
Elliott Hughes04434732023-01-10 22:59:40 +0000289 if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800290 ALOGD("Failure seeking to central dir offset %" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700291 mEOCD.mCentralDirOffset);
Elliott Hughes04434732023-01-10 22:59:40 +0000292 return UNKNOWN_ERROR;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700293 }
294
295 /*
296 * Loop through and read the central dir entries.
297 */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800298 ALOGV("Scanning %" PRIu16 " entries...\n", mEOCD.mTotalNumEntries);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700299 int entry;
300 for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
301 ZipEntry* pEntry = new ZipEntry;
302
303 result = pEntry->initFromCDE(mZipFp);
Elliott Hughesad7d5622018-10-08 11:19:28 -0700304 if (result != OK) {
Steve Block15fab1a2011-12-20 16:25:43 +0000305 ALOGD("initFromCDE failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700306 delete pEntry;
Elliott Hughes04434732023-01-10 22:59:40 +0000307 return result;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700308 }
309
310 mEntries.add(pEntry);
311 }
312
313
314 /*
315 * If all went well, we should now be back at the EOCD.
316 */
317 {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800318 uint8_t checkBuf[4];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700319 if (fread(checkBuf, 1, 4, mZipFp) != 4) {
Colin Cross383e4822021-01-08 11:06:24 -0800320 if (feof(mZipFp)) {
321 ALOGW("fread EOCD failed, unexpected EOF");
322 } else {
323 ALOGW("fread EOCD failed, %s", strerror(errno));
324 }
Elliott Hughes04434732023-01-10 22:59:40 +0000325 return INVALID_OPERATION;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700326 }
327 if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000328 ALOGD("EOCD read check failed\n");
Elliott Hughes04434732023-01-10 22:59:40 +0000329 return UNKNOWN_ERROR;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700330 }
Steve Block2da72c62011-10-20 11:56:20 +0100331 ALOGV("+++ EOCD read check passed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700332 }
333
Elliott Hughes04434732023-01-10 22:59:40 +0000334 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700335}
336
337
338/*
339 * Add a new file to the archive.
340 *
341 * This requires creating and populating a ZipEntry structure, and copying
342 * the data into the file at the appropriate position. The "appropriate
343 * position" is the current location of the central directory, which we
344 * casually overwrite (we can put it back later).
345 *
346 * If we were concerned about safety, we would want to make all changes
347 * in a temp file and then overwrite the original after everything was
348 * safely written. Not really a concern for us.
349 */
350status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
Narayan Kamathb46507f2017-02-16 10:53:09 +0000351 const char* storageName, int compressionMethod, ZipEntry** ppEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700352{
353 ZipEntry* pEntry = NULL;
Elliott Hughesad7d5622018-10-08 11:19:28 -0700354 status_t result = OK;
Elliott Hughes04434732023-01-10 22:59:40 +0000355 off_t lfhPosn, startPosn, endPosn, uncompressedLen;
Chih-Hung Hsiehfed64e32022-12-20 13:50:57 -0800356 uint32_t crc = 0;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700357 time_t modWhen;
358
359 if (mReadOnly)
360 return INVALID_OPERATION;
361
362 assert(compressionMethod == ZipEntry::kCompressDeflated ||
363 compressionMethod == ZipEntry::kCompressStored);
364
365 /* make sure we're in a reasonable state */
366 assert(mZipFp != NULL);
367 assert(mEntries.size() == mEOCD.mTotalNumEntries);
368
369 /* make sure it doesn't already exist */
370 if (getEntryByName(storageName) != NULL)
371 return ALREADY_EXISTS;
372
Elliott Hughes04434732023-01-10 22:59:40 +0000373 FILE* inputFp = NULL;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700374 if (!data) {
375 inputFp = fopen(fileName, FILE_OPEN_RO);
376 if (inputFp == NULL)
377 return errnoToStatus(errno);
378 }
379
Elliott Hughes04434732023-01-10 22:59:40 +0000380 if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700381 result = UNKNOWN_ERROR;
382 goto bail;
383 }
384
385 pEntry = new ZipEntry;
386 pEntry->initNew(storageName, NULL);
387
388 /*
389 * From here on out, failures are more interesting.
390 */
391 mNeedCDRewrite = true;
392
393 /*
394 * Write the LFH, even though it's still mostly blank. We need it
395 * as a place-holder. In theory the LFH isn't necessary, but in
396 * practice some utilities demand it.
397 */
Elliott Hughes04434732023-01-10 22:59:40 +0000398 lfhPosn = ftello(mZipFp);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700399 pEntry->mLFH.write(mZipFp);
Elliott Hughes04434732023-01-10 22:59:40 +0000400 startPosn = ftello(mZipFp);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700401
402 /*
403 * Copy the data in, possibly compressing it as we go.
404 */
Narayan Kamathb46507f2017-02-16 10:53:09 +0000405 if (compressionMethod == ZipEntry::kCompressDeflated) {
406 bool failed = false;
407 result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
Elliott Hughesad7d5622018-10-08 11:19:28 -0700408 if (result != OK) {
Narayan Kamathb46507f2017-02-16 10:53:09 +0000409 ALOGD("compression failed, storing\n");
410 failed = true;
411 } else {
412 /*
413 * Make sure it has compressed "enough". This probably ought
414 * to be set through an API call, but I don't expect our
415 * criteria to change over time.
416 */
Elliott Hughes04434732023-01-10 22:59:40 +0000417 off_t src = inputFp ? ftello(inputFp) : size;
418 off_t dst = ftello(mZipFp) - startPosn;
Narayan Kamathb46507f2017-02-16 10:53:09 +0000419 if (dst + (dst / 10) > src) {
Elliott Hughes04434732023-01-10 22:59:40 +0000420 ALOGD("insufficient compression (src=%lld dst=%lld), storing\n",
421 (long long) src, (long long) dst);
Narayan Kamathb46507f2017-02-16 10:53:09 +0000422 failed = true;
423 }
424 }
425
426 if (failed) {
427 compressionMethod = ZipEntry::kCompressStored;
428 if (inputFp) rewind(inputFp);
Elliott Hughes04434732023-01-10 22:59:40 +0000429 fseeko(mZipFp, startPosn, SEEK_SET);
Narayan Kamathb46507f2017-02-16 10:53:09 +0000430 /* fall through to kCompressStored case */
431 }
432 }
433 /* handle "no compression" request, or failed compression from above */
434 if (compressionMethod == ZipEntry::kCompressStored) {
435 if (inputFp) {
436 result = copyFpToFp(mZipFp, inputFp, &crc);
437 } else {
438 result = copyDataToFp(mZipFp, data, size, &crc);
439 }
Elliott Hughesad7d5622018-10-08 11:19:28 -0700440 if (result != OK) {
Narayan Kamathb46507f2017-02-16 10:53:09 +0000441 // don't need to truncate; happens in CDE rewrite
442 ALOGD("failed copying data in\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700443 goto bail;
444 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700445 }
446
Narayan Kamathb46507f2017-02-16 10:53:09 +0000447 // currently seeked to end of file
Elliott Hughes04434732023-01-10 22:59:40 +0000448 uncompressedLen = inputFp ? ftello(inputFp) : size;
Narayan Kamathb46507f2017-02-16 10:53:09 +0000449
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700450 /*
451 * We could write the "Data Descriptor", but there doesn't seem to
452 * be any point since we're going to go back and write the LFH.
453 *
454 * Update file offsets.
455 */
Elliott Hughes04434732023-01-10 22:59:40 +0000456 endPosn = ftello(mZipFp); // seeked to end of compressed data
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700457
458 /*
459 * Success! Fill out new values.
460 */
461 pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
462 compressionMethod);
Dan Willemsenb589ae42015-10-29 21:26:18 +0000463 modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
464 pEntry->setModWhen(modWhen);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700465 pEntry->setLFHOffset(lfhPosn);
466 mEOCD.mNumEntries++;
467 mEOCD.mTotalNumEntries++;
468 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
469 mEOCD.mCentralDirOffset = endPosn;
470
471 /*
472 * Go back and write the LFH.
473 */
Elliott Hughes04434732023-01-10 22:59:40 +0000474 if (fseeko(mZipFp, lfhPosn, SEEK_SET) != 0) {
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700475 result = UNKNOWN_ERROR;
476 goto bail;
477 }
478 pEntry->mLFH.write(mZipFp);
479
480 /*
481 * Add pEntry to the list.
482 */
483 mEntries.add(pEntry);
484 if (ppEntry != NULL)
485 *ppEntry = pEntry;
486 pEntry = NULL;
487
488bail:
489 if (inputFp != NULL)
490 fclose(inputFp);
491 delete pEntry;
492 return result;
493}
494
495/*
Fabien Sanglarda7206352020-10-20 15:47:10 -0700496 * Based on the current position in the output zip, assess where the entry
497 * payload will end up if written as-is. If alignment is not satisfactory,
498 * add some padding in the extra field.
499 *
500 */
501status_t ZipFile::alignEntry(android::ZipEntry* pEntry, uint32_t alignTo){
502 if (alignTo == 0 || alignTo == 1)
503 return OK;
504
505 // Calculate where the entry payload offset will end up if we were to write
506 // it as-is.
Elliott Hughes04434732023-01-10 22:59:40 +0000507 uint64_t expectedPayloadOffset = ftello(mZipFp) +
Fabien Sanglarda7206352020-10-20 15:47:10 -0700508 android::ZipEntry::LocalFileHeader::kLFHLen +
509 pEntry->mLFH.mFileNameLength +
510 pEntry->mLFH.mExtraFieldLength;
511
512 // If the alignment is not what was requested, add some padding in the extra
513 // so the payload ends up where is requested.
514 uint64_t alignDiff = alignTo - (expectedPayloadOffset % alignTo);
Fabien Sanglarddf73d1b2021-09-14 14:18:31 -0700515 if (alignDiff == alignTo)
Fabien Sanglarda7206352020-10-20 15:47:10 -0700516 return OK;
517
518 return pEntry->addPadding(alignDiff);
519}
520
521/*
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700522 * Add an entry by copying it from another zip file. If "padding" is
523 * nonzero, the specified number of bytes will be added to the "extra"
524 * field in the header.
525 *
526 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
527 */
528status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
Fabien Sanglarda7206352020-10-20 15:47:10 -0700529 int alignTo, ZipEntry** ppEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700530{
531 ZipEntry* pEntry = NULL;
532 status_t result;
Elliott Hughes04434732023-01-10 22:59:40 +0000533 off_t lfhPosn, endPosn;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700534
535 if (mReadOnly)
536 return INVALID_OPERATION;
537
538 /* make sure we're in a reasonable state */
539 assert(mZipFp != NULL);
540 assert(mEntries.size() == mEOCD.mTotalNumEntries);
541
Elliott Hughes04434732023-01-10 22:59:40 +0000542 if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700543 result = UNKNOWN_ERROR;
544 goto bail;
545 }
546
547 pEntry = new ZipEntry;
548 if (pEntry == NULL) {
549 result = NO_MEMORY;
550 goto bail;
551 }
552
Aurimas Liutikasaf1d7412016-02-11 18:11:21 -0800553 result = pEntry->initFromExternal(pSourceEntry);
Elliott Hughesad7d5622018-10-08 11:19:28 -0700554 if (result != OK)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700555 goto bail;
Fabien Sanglarda7206352020-10-20 15:47:10 -0700556
557 result = alignEntry(pEntry, alignTo);
558 if (result != OK)
559 goto bail;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700560
561 /*
562 * From here on out, failures are more interesting.
563 */
564 mNeedCDRewrite = true;
565
566 /*
567 * Write the LFH. Since we're not recompressing the data, we already
568 * have all of the fields filled out.
569 */
Elliott Hughes04434732023-01-10 22:59:40 +0000570 lfhPosn = ftello(mZipFp);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700571 pEntry->mLFH.write(mZipFp);
572
573 /*
574 * Copy the data over.
575 *
576 * If the "has data descriptor" flag is set, we want to copy the DD
577 * fields as well. This is a fixed-size area immediately following
578 * the data.
579 */
Elliott Hughes04434732023-01-10 22:59:40 +0000580 if (fseeko(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) {
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700581 result = UNKNOWN_ERROR;
582 goto bail;
583 }
584
585 off_t copyLen;
586 copyLen = pSourceEntry->getCompressedLen();
587 if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
588 copyLen += ZipEntry::kDataDescriptorLen;
589
590 if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
Elliott Hughesad7d5622018-10-08 11:19:28 -0700591 != OK)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700592 {
Steve Blockc0b74df2012-01-05 23:28:01 +0000593 ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700594 result = UNKNOWN_ERROR;
595 goto bail;
596 }
597
598 /*
599 * Update file offsets.
600 */
Elliott Hughes04434732023-01-10 22:59:40 +0000601 endPosn = ftello(mZipFp);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700602
603 /*
604 * Success! Fill out new values.
605 */
606 pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset
607 mEOCD.mNumEntries++;
608 mEOCD.mTotalNumEntries++;
609 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
610 mEOCD.mCentralDirOffset = endPosn;
611
612 /*
613 * Add pEntry to the list.
614 */
615 mEntries.add(pEntry);
616 if (ppEntry != NULL)
617 *ppEntry = pEntry;
618 pEntry = NULL;
619
Elliott Hughesad7d5622018-10-08 11:19:28 -0700620 result = OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700621
622bail:
623 delete pEntry;
624 return result;
625}
626
627/*
Raph Levien093d04c2014-07-07 16:00:29 -0700628 * Add an entry by copying it from another zip file, recompressing with
629 * Zopfli if already compressed.
630 *
631 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
632 */
633status_t ZipFile::addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
Dan Willemsenb589ae42015-10-29 21:26:18 +0000634 ZipEntry** ppEntry)
Raph Levien093d04c2014-07-07 16:00:29 -0700635{
636 ZipEntry* pEntry = NULL;
637 status_t result;
Elliott Hughes04434732023-01-10 22:59:40 +0000638 off_t lfhPosn, uncompressedLen;
Raph Levien093d04c2014-07-07 16:00:29 -0700639
640 if (mReadOnly)
641 return INVALID_OPERATION;
642
643 /* make sure we're in a reasonable state */
644 assert(mZipFp != NULL);
645 assert(mEntries.size() == mEOCD.mTotalNumEntries);
646
Elliott Hughes04434732023-01-10 22:59:40 +0000647 if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
Raph Levien093d04c2014-07-07 16:00:29 -0700648 result = UNKNOWN_ERROR;
649 goto bail;
650 }
651
652 pEntry = new ZipEntry;
653 if (pEntry == NULL) {
654 result = NO_MEMORY;
655 goto bail;
656 }
657
Aurimas Liutikasaf1d7412016-02-11 18:11:21 -0800658 result = pEntry->initFromExternal(pSourceEntry);
Elliott Hughesad7d5622018-10-08 11:19:28 -0700659 if (result != OK)
Raph Levien093d04c2014-07-07 16:00:29 -0700660 goto bail;
661
662 /*
663 * From here on out, failures are more interesting.
664 */
665 mNeedCDRewrite = true;
666
667 /*
668 * Write the LFH, even though it's still mostly blank. We need it
669 * as a place-holder. In theory the LFH isn't necessary, but in
670 * practice some utilities demand it.
671 */
Elliott Hughes04434732023-01-10 22:59:40 +0000672 lfhPosn = ftello(mZipFp);
Raph Levien093d04c2014-07-07 16:00:29 -0700673 pEntry->mLFH.write(mZipFp);
Raph Levien093d04c2014-07-07 16:00:29 -0700674
675 /*
676 * Copy the data over.
677 *
678 * If the "has data descriptor" flag is set, we want to copy the DD
679 * fields as well. This is a fixed-size area immediately following
680 * the data.
681 */
Elliott Hughes04434732023-01-10 22:59:40 +0000682 if (fseeko(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) {
Raph Levien093d04c2014-07-07 16:00:29 -0700683 result = UNKNOWN_ERROR;
684 goto bail;
685 }
686
687 uncompressedLen = pSourceEntry->getUncompressedLen();
688
689 if (pSourceEntry->isCompressed()) {
690 void *buf = pSourceZip->uncompress(pSourceEntry);
691 if (buf == NULL) {
692 result = NO_MEMORY;
693 goto bail;
694 }
Elliott Hughes04434732023-01-10 22:59:40 +0000695 off_t startPosn = ftello(mZipFp);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800696 uint32_t crc;
Elliott Hughesad7d5622018-10-08 11:19:28 -0700697 if (compressFpToFp(mZipFp, NULL, buf, uncompressedLen, &crc) != OK) {
Raph Levien093d04c2014-07-07 16:00:29 -0700698 ALOGW("recompress of '%s' failed\n", pEntry->mCDE.mFileName);
699 result = UNKNOWN_ERROR;
700 free(buf);
701 goto bail;
702 }
Elliott Hughes04434732023-01-10 22:59:40 +0000703 off_t endPosn = ftello(mZipFp);
Raph Levien093d04c2014-07-07 16:00:29 -0700704 pEntry->setDataInfo(uncompressedLen, endPosn - startPosn,
705 pSourceEntry->getCRC32(), ZipEntry::kCompressDeflated);
706 free(buf);
707 } else {
Elliott Hughes04434732023-01-10 22:59:40 +0000708 off_t copyLen = pSourceEntry->getCompressedLen();
Raph Levien093d04c2014-07-07 16:00:29 -0700709 if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
710 copyLen += ZipEntry::kDataDescriptorLen;
711
712 if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
Elliott Hughesad7d5622018-10-08 11:19:28 -0700713 != OK)
Raph Levien093d04c2014-07-07 16:00:29 -0700714 {
715 ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
716 result = UNKNOWN_ERROR;
717 goto bail;
718 }
719 }
720
721 /*
Raph Levien093d04c2014-07-07 16:00:29 -0700722 * Success! Fill out new values.
723 */
724 pEntry->setLFHOffset(lfhPosn);
725 mEOCD.mNumEntries++;
726 mEOCD.mTotalNumEntries++;
727 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
Elliott Hughes04434732023-01-10 22:59:40 +0000728 mEOCD.mCentralDirOffset = ftello(mZipFp);
Raph Levien093d04c2014-07-07 16:00:29 -0700729
730 /*
731 * Go back and write the LFH.
732 */
Elliott Hughes04434732023-01-10 22:59:40 +0000733 if (fseeko(mZipFp, lfhPosn, SEEK_SET) != 0) {
Raph Levien093d04c2014-07-07 16:00:29 -0700734 result = UNKNOWN_ERROR;
735 goto bail;
736 }
737 pEntry->mLFH.write(mZipFp);
738
739 /*
740 * Add pEntry to the list.
741 */
742 mEntries.add(pEntry);
743 if (ppEntry != NULL)
744 *ppEntry = pEntry;
745 pEntry = NULL;
746
Elliott Hughesad7d5622018-10-08 11:19:28 -0700747 result = OK;
Raph Levien093d04c2014-07-07 16:00:29 -0700748
749bail:
750 delete pEntry;
751 return result;
752}
753
754/*
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700755 * Copy all of the bytes in "src" to "dst".
756 *
757 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
758 * will be seeked immediately past the data.
759 */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800760status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700761{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800762 uint8_t tmpBuf[32768];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700763 size_t count;
764
765 *pCRC32 = crc32(0L, Z_NULL, 0);
766
767 while (1) {
768 count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
Colin Cross383e4822021-01-08 11:06:24 -0800769 if (ferror(srcFp) || ferror(dstFp)) {
770 status_t status = errnoToStatus(errno);
771 ALOGW("fread %zu bytes failed, %s", count, strerror(errno));
772 return status;
773 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700774 if (count == 0)
775 break;
776
777 *pCRC32 = crc32(*pCRC32, tmpBuf, count);
778
779 if (fwrite(tmpBuf, 1, count, dstFp) != count) {
Colin Cross383e4822021-01-08 11:06:24 -0800780 ALOGW("fwrite %zu bytes failed, %s", count, strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700781 return UNKNOWN_ERROR;
782 }
783 }
784
Elliott Hughesad7d5622018-10-08 11:19:28 -0700785 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700786}
787
788/*
789 * Copy all of the bytes in "src" to "dst".
790 *
791 * On exit, "dstFp" will be seeked immediately past the data.
792 */
793status_t ZipFile::copyDataToFp(FILE* dstFp,
Dan Willemsen41bc4242015-11-04 14:08:20 -0800794 const void* data, size_t size, uint32_t* pCRC32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700795{
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700796 *pCRC32 = crc32(0L, Z_NULL, 0);
797 if (size > 0) {
798 *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
799 if (fwrite(data, 1, size, dstFp) != size) {
Colin Cross383e4822021-01-08 11:06:24 -0800800 ALOGW("fwrite %zu bytes failed, %s", size, strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700801 return UNKNOWN_ERROR;
802 }
803 }
804
Elliott Hughesad7d5622018-10-08 11:19:28 -0700805 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700806}
807
808/*
809 * Copy some of the bytes in "src" to "dst".
810 *
811 * If "pCRC32" is NULL, the CRC will not be computed.
812 *
813 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
814 * will be seeked immediately past the data just written.
815 */
Chih-Hung Hsieh0c0d9282018-08-10 15:14:26 -0700816status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, size_t length,
Dan Willemsen41bc4242015-11-04 14:08:20 -0800817 uint32_t* pCRC32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700818{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800819 uint8_t tmpBuf[32768];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700820 size_t count;
821
822 if (pCRC32 != NULL)
823 *pCRC32 = crc32(0L, Z_NULL, 0);
824
825 while (length) {
Chih-Hung Hsieh0c0d9282018-08-10 15:14:26 -0700826 size_t readSize;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800827
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700828 readSize = sizeof(tmpBuf);
829 if (readSize > length)
830 readSize = length;
831
832 count = fread(tmpBuf, 1, readSize, srcFp);
Chih-Hung Hsieh0c0d9282018-08-10 15:14:26 -0700833 if (count != readSize) { // error or unexpected EOF
Colin Cross383e4822021-01-08 11:06:24 -0800834 if (feof(srcFp)) {
835 ALOGW("fread %zu bytes failed, unexpected EOF", readSize);
836 } else {
837 ALOGW("fread %zu bytes failed, %s", readSize, strerror(errno));
838 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700839 return UNKNOWN_ERROR;
840 }
841
842 if (pCRC32 != NULL)
843 *pCRC32 = crc32(*pCRC32, tmpBuf, count);
844
845 if (fwrite(tmpBuf, 1, count, dstFp) != count) {
Colin Cross383e4822021-01-08 11:06:24 -0800846 ALOGW("fwrite %zu bytes failed, %s", count, strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700847 return UNKNOWN_ERROR;
848 }
849
850 length -= readSize;
851 }
852
Elliott Hughesad7d5622018-10-08 11:19:28 -0700853 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700854}
855
856/*
857 * Compress all of the data in "srcFp" and write it to "dstFp".
858 *
859 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
860 * will be seeked immediately past the compressed data.
861 */
862status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
Dan Willemsen41bc4242015-11-04 14:08:20 -0800863 const void* data, size_t size, uint32_t* pCRC32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700864{
Elliott Hughesad7d5622018-10-08 11:19:28 -0700865 status_t result = OK;
Raph Levien093d04c2014-07-07 16:00:29 -0700866 const size_t kBufSize = 1024 * 1024;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800867 uint8_t* inBuf = NULL;
868 uint8_t* outBuf = NULL;
Raph Levien093d04c2014-07-07 16:00:29 -0700869 size_t outSize = 0;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700870 bool atEof = false; // no feof() aviailable yet
Dan Willemsen41bc4242015-11-04 14:08:20 -0800871 uint32_t crc;
Raph Levien093d04c2014-07-07 16:00:29 -0700872 ZopfliOptions options;
873 unsigned char bp = 0;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700874
Raph Levien093d04c2014-07-07 16:00:29 -0700875 ZopfliInitOptions(&options);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700876
877 crc = crc32(0L, Z_NULL, 0);
878
Raph Levien093d04c2014-07-07 16:00:29 -0700879 if (data) {
880 crc = crc32(crc, (const unsigned char*)data, size);
881 ZopfliDeflate(&options, 2, true, (const unsigned char*)data, size, &bp,
882 &outBuf, &outSize);
883 } else {
884 /*
885 * Create an input buffer and an output buffer.
886 */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800887 inBuf = new uint8_t[kBufSize];
Raph Levien093d04c2014-07-07 16:00:29 -0700888 if (inBuf == NULL) {
889 result = NO_MEMORY;
890 goto bail;
891 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700892
Raph Levien093d04c2014-07-07 16:00:29 -0700893 /*
894 * Loop while we have data.
895 */
896 do {
897 size_t getSize;
898 getSize = fread(inBuf, 1, kBufSize, srcFp);
899 if (ferror(srcFp)) {
900 ALOGD("deflate read failed (errno=%d)\n", errno);
Yunlian Jiang221c1c02016-10-05 10:58:37 -0700901 result = UNKNOWN_ERROR;
Raph Levien093d04c2014-07-07 16:00:29 -0700902 delete[] inBuf;
903 goto bail;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700904 }
905 if (getSize < kBufSize) {
Colin Cross383e4822021-01-08 11:06:24 -0800906 ALOGV("+++ got %zu bytes, EOF reached\n", getSize);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700907 atEof = true;
908 }
909
910 crc = crc32(crc, inBuf, getSize);
Raph Levien093d04c2014-07-07 16:00:29 -0700911 ZopfliDeflate(&options, 2, atEof, inBuf, getSize, &bp, &outBuf, &outSize);
912 } while (!atEof);
913 delete[] inBuf;
914 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700915
Colin Cross383e4822021-01-08 11:06:24 -0800916 ALOGV("+++ writing %zu bytes\n", outSize);
Raph Levien093d04c2014-07-07 16:00:29 -0700917 if (fwrite(outBuf, 1, outSize, dstFp) != outSize) {
Colin Cross383e4822021-01-08 11:06:24 -0800918 ALOGW("fwrite %zu bytes failed, %s", outSize, strerror(errno));
Yunlian Jiang221c1c02016-10-05 10:58:37 -0700919 result = UNKNOWN_ERROR;
Raph Levien093d04c2014-07-07 16:00:29 -0700920 goto bail;
921 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700922
923 *pCRC32 = crc;
924
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700925bail:
Raph Levien093d04c2014-07-07 16:00:29 -0700926 free(outBuf);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700927
928 return result;
929}
930
931/*
932 * Mark an entry as deleted.
933 *
934 * We will eventually need to crunch the file down, but if several files
935 * are being removed (perhaps as part of an "update" process) we can make
936 * things considerably faster by deferring the removal to "flush" time.
937 */
938status_t ZipFile::remove(ZipEntry* pEntry)
939{
940 /*
941 * Should verify that pEntry is actually part of this archive, and
942 * not some stray ZipEntry from a different file.
943 */
944
945 /* mark entry as deleted, and mark archive as dirty */
946 pEntry->setDeleted();
947 mNeedCDRewrite = true;
Elliott Hughesad7d5622018-10-08 11:19:28 -0700948 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700949}
950
951/*
952 * Flush any pending writes.
953 *
954 * In particular, this will crunch out deleted entries, and write the
955 * Central Directory and EOCD if we have stomped on them.
956 */
957status_t ZipFile::flush(void)
958{
Elliott Hughesad7d5622018-10-08 11:19:28 -0700959 status_t result = OK;
Elliott Hughes04434732023-01-10 22:59:40 +0000960 off_t eocdPosn;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700961 int i, count;
962
963 if (mReadOnly)
964 return INVALID_OPERATION;
965 if (!mNeedCDRewrite)
Elliott Hughesad7d5622018-10-08 11:19:28 -0700966 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700967
968 assert(mZipFp != NULL);
969
970 result = crunchArchive();
Elliott Hughesad7d5622018-10-08 11:19:28 -0700971 if (result != OK)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700972 return result;
973
Elliott Hughes04434732023-01-10 22:59:40 +0000974 if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) return UNKNOWN_ERROR;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700975
976 count = mEntries.size();
977 for (i = 0; i < count; i++) {
978 ZipEntry* pEntry = mEntries[i];
979 pEntry->mCDE.write(mZipFp);
980 }
981
Elliott Hughes04434732023-01-10 22:59:40 +0000982 eocdPosn = ftello(mZipFp);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700983 mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
984
985 mEOCD.write(mZipFp);
986
987 /*
988 * If we had some stuff bloat up during compression and get replaced
989 * with plain files, or if we deleted some entries, there's a lot
990 * of wasted space at the end of the file. Remove it now.
991 */
Elliott Hughes04434732023-01-10 22:59:40 +0000992 if (ftruncate(fileno(mZipFp), ftello(mZipFp)) != 0) {
993 ALOGW("ftruncate failed %lld: %s\n", (long long) ftello(mZipFp), strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700994 // not fatal
995 }
996
997 /* should we clear the "newly added" flag in all entries now? */
998
999 mNeedCDRewrite = false;
Elliott Hughesad7d5622018-10-08 11:19:28 -07001000 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001001}
1002
1003/*
1004 * Crunch deleted files out of an archive by shifting the later files down.
1005 *
1006 * Because we're not using a temp file, we do the operation inside the
1007 * current file.
1008 */
1009status_t ZipFile::crunchArchive(void)
1010{
Elliott Hughesad7d5622018-10-08 11:19:28 -07001011 status_t result = OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001012 int i, count;
1013 long delCount, adjust;
1014
1015#if 0
1016 printf("CONTENTS:\n");
1017 for (i = 0; i < (int) mEntries.size(); i++) {
1018 printf(" %d: lfhOff=%ld del=%d\n",
1019 i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
1020 }
1021 printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset);
1022#endif
1023
1024 /*
1025 * Roll through the set of files, shifting them as appropriate. We
1026 * could probably get a slight performance improvement by sliding
1027 * multiple files down at once (because we could use larger reads
1028 * when operating on batches of small files), but it's not that useful.
1029 */
1030 count = mEntries.size();
1031 delCount = adjust = 0;
1032 for (i = 0; i < count; i++) {
1033 ZipEntry* pEntry = mEntries[i];
1034 long span;
1035
1036 if (pEntry->getLFHOffset() != 0) {
1037 long nextOffset;
1038
1039 /* Get the length of this entry by finding the offset
1040 * of the next entry. Directory entries don't have
1041 * file offsets, so we need to find the next non-directory
1042 * entry.
1043 */
1044 nextOffset = 0;
1045 for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
1046 nextOffset = mEntries[ii]->getLFHOffset();
1047 if (nextOffset == 0)
1048 nextOffset = mEOCD.mCentralDirOffset;
1049 span = nextOffset - pEntry->getLFHOffset();
1050
1051 assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
1052 } else {
1053 /* This is a directory entry. It doesn't have
1054 * any actual file contents, so there's no need to
1055 * move anything.
1056 */
1057 span = 0;
1058 }
1059
1060 //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
1061 // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
1062
1063 if (pEntry->getDeleted()) {
1064 adjust += span;
1065 delCount++;
1066
1067 delete pEntry;
1068 mEntries.removeAt(i);
1069
1070 /* adjust loop control */
1071 count--;
1072 i--;
1073 } else if (span != 0 && adjust > 0) {
1074 /* shuffle this entry back */
1075 //printf("+++ Shuffling '%s' back %ld\n",
1076 // pEntry->getFileName(), adjust);
1077 result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
1078 pEntry->getLFHOffset(), span);
Elliott Hughesad7d5622018-10-08 11:19:28 -07001079 if (result != OK) {
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001080 /* this is why you use a temp file */
Steve Blockca1df5a2012-01-08 10:18:46 +00001081 ALOGE("error during crunch - archive is toast\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001082 return result;
1083 }
1084
1085 pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
1086 }
1087 }
1088
1089 /*
1090 * Fix EOCD info. We have to wait until the end to do some of this
1091 * because we use mCentralDirOffset to determine "span" for the
1092 * last entry.
1093 */
1094 mEOCD.mCentralDirOffset -= adjust;
1095 mEOCD.mNumEntries -= delCount;
1096 mEOCD.mTotalNumEntries -= delCount;
1097 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
1098
1099 assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
1100 assert(mEOCD.mNumEntries == count);
1101
1102 return result;
1103}
1104
1105/*
1106 * Works like memmove(), but on pieces of a file.
1107 */
1108status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
1109{
1110 if (dst == src || n <= 0)
Elliott Hughesad7d5622018-10-08 11:19:28 -07001111 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001112
Dan Willemsen41bc4242015-11-04 14:08:20 -08001113 uint8_t readBuf[32768];
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001114
1115 if (dst < src) {
1116 /* shift stuff toward start of file; must read from start */
1117 while (n != 0) {
1118 size_t getSize = sizeof(readBuf);
1119 if (getSize > n)
1120 getSize = n;
1121
Elliott Hughes04434732023-01-10 22:59:40 +00001122 if (fseeko(fp, src, SEEK_SET) != 0) {
1123 ALOGW("filemove src seek %lld failed, %s",
1124 (long long) src, strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001125 return UNKNOWN_ERROR;
1126 }
1127
1128 if (fread(readBuf, 1, getSize, fp) != getSize) {
Colin Cross383e4822021-01-08 11:06:24 -08001129 if (feof(fp)) {
Elliott Hughes04434732023-01-10 22:59:40 +00001130 ALOGW("fread %zu bytes off=%lld failed, unexpected EOF",
1131 getSize, (long long) src);
Colin Cross383e4822021-01-08 11:06:24 -08001132 } else {
Elliott Hughes04434732023-01-10 22:59:40 +00001133 ALOGW("fread %zu bytes off=%lld failed, %s",
1134 getSize, (long long) src, strerror(errno));
Colin Cross383e4822021-01-08 11:06:24 -08001135 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001136 return UNKNOWN_ERROR;
1137 }
1138
Elliott Hughes04434732023-01-10 22:59:40 +00001139 if (fseeko(fp, dst, SEEK_SET) != 0) {
1140 ALOGW("filemove dst seek %lld failed, %s",
1141 (long long) dst, strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001142 return UNKNOWN_ERROR;
1143 }
1144
1145 if (fwrite(readBuf, 1, getSize, fp) != getSize) {
Elliott Hughes04434732023-01-10 22:59:40 +00001146 ALOGW("filemove write %zu off=%lld failed, %s",
1147 getSize, (long long) dst, strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001148 return UNKNOWN_ERROR;
1149 }
1150
1151 src += getSize;
1152 dst += getSize;
1153 n -= getSize;
1154 }
1155 } else {
1156 /* shift stuff toward end of file; must read from end */
1157 assert(false); // write this someday, maybe
1158 return UNKNOWN_ERROR;
1159 }
1160
Elliott Hughesad7d5622018-10-08 11:19:28 -07001161 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001162}
1163
1164
1165/*
1166 * Get the modification time from a file descriptor.
1167 */
1168time_t ZipFile::getModTime(int fd)
1169{
1170 struct stat sb;
1171
1172 if (fstat(fd, &sb) < 0) {
Steve Block15fab1a2011-12-20 16:25:43 +00001173 ALOGD("HEY: fstat on fd %d failed\n", fd);
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001174 return (time_t) -1;
1175 }
1176
1177 return sb.st_mtime;
1178}
1179
1180
1181#if 0 /* this is a bad idea */
1182/*
1183 * Get a copy of the Zip file descriptor.
1184 *
1185 * We don't allow this if the file was opened read-write because we tend
1186 * to leave the file contents in an uncertain state between calls to
1187 * flush(). The duplicated file descriptor should only be valid for reads.
1188 */
1189int ZipFile::getZipFd(void) const
1190{
1191 if (!mReadOnly)
1192 return INVALID_OPERATION;
1193 assert(mZipFp != NULL);
1194
1195 int fd;
1196 fd = dup(fileno(mZipFp));
1197 if (fd < 0) {
Steve Block15fab1a2011-12-20 16:25:43 +00001198 ALOGD("didn't work, errno=%d\n", errno);
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001199 }
1200
1201 return fd;
1202}
1203#endif
1204
1205
1206#if 0
1207/*
1208 * Expand data.
1209 */
1210bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
1211{
1212 return false;
1213}
1214#endif
1215
Narayan Kamath0e4110e2017-10-26 18:00:13 +01001216class BufferWriter : public zip_archive::Writer {
1217 public:
1218 BufferWriter(void* buf, size_t size) : Writer(),
1219 buf_(reinterpret_cast<uint8_t*>(buf)), size_(size), bytes_written_(0) {}
1220
1221 bool Append(uint8_t* buf, size_t buf_size) override {
1222 if (bytes_written_ + buf_size > size_) {
1223 return false;
1224 }
1225
1226 memcpy(buf_ + bytes_written_, buf, buf_size);
1227 bytes_written_ += buf_size;
1228 return true;
1229 }
1230
1231 private:
1232 uint8_t* const buf_;
1233 const size_t size_;
1234 size_t bytes_written_;
1235};
1236
1237class FileReader : public zip_archive::Reader {
1238 public:
1239 FileReader(FILE* fp) : Reader(), fp_(fp), current_offset_(0) {
1240 }
1241
Tianjie2751d2b2020-04-01 20:32:36 -07001242 bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
Narayan Kamath0e4110e2017-10-26 18:00:13 +01001243 // Data is usually requested sequentially, so this helps avoid pointless
Elliott Hughes04434732023-01-10 22:59:40 +00001244 // seeks every time we perform a read. There's an impedence mismatch
Narayan Kamath0e4110e2017-10-26 18:00:13 +01001245 // here because the original API was designed around pread and pwrite.
1246 if (offset != current_offset_) {
Elliott Hughes04434732023-01-10 22:59:40 +00001247 if (fseeko(fp_, offset, SEEK_SET) != 0) {
Narayan Kamath0e4110e2017-10-26 18:00:13 +01001248 return false;
1249 }
1250
1251 current_offset_ = offset;
1252 }
1253
1254 size_t read = fread(buf, 1, len, fp_);
1255 if (read != len) {
1256 return false;
1257 }
1258
1259 current_offset_ += read;
1260 return true;
1261 }
1262
1263 private:
1264 FILE* fp_;
Tianjie2751d2b2020-04-01 20:32:36 -07001265 mutable off64_t current_offset_;
Narayan Kamath0e4110e2017-10-26 18:00:13 +01001266};
1267
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001268// free the memory when you're done
Raph Levien093d04c2014-07-07 16:00:29 -07001269void* ZipFile::uncompress(const ZipEntry* entry) const
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001270{
1271 size_t unlen = entry->getUncompressedLen();
1272 size_t clen = entry->getCompressedLen();
1273
1274 void* buf = malloc(unlen);
1275 if (buf == NULL) {
1276 return NULL;
1277 }
1278
Elliott Hughes04434732023-01-10 22:59:40 +00001279 fseeko(mZipFp, 0, SEEK_SET);
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001280
1281 off_t offset = entry->getFileOffset();
Elliott Hughes04434732023-01-10 22:59:40 +00001282 if (fseeko(mZipFp, offset, SEEK_SET) != 0) {
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001283 goto bail;
1284 }
1285
1286 switch (entry->getCompressionMethod())
1287 {
1288 case ZipEntry::kCompressStored: {
1289 ssize_t amt = fread(buf, 1, unlen, mZipFp);
1290 if (amt != (ssize_t)unlen) {
1291 goto bail;
1292 }
1293#if 0
1294 printf("data...\n");
1295 const unsigned char* p = (unsigned char*)buf;
1296 const unsigned char* end = p+unlen;
1297 for (int i=0; i<32 && p < end; i++) {
1298 printf("0x%08x ", (int)(offset+(i*0x10)));
1299 for (int j=0; j<0x10 && p < end; j++) {
1300 printf(" %02x", *p);
1301 p++;
1302 }
1303 printf("\n");
1304 }
1305#endif
1306
1307 }
1308 break;
1309 case ZipEntry::kCompressDeflated: {
Narayan Kamath0e4110e2017-10-26 18:00:13 +01001310 const FileReader reader(mZipFp);
1311 BufferWriter writer(buf, unlen);
1312 if (zip_archive::Inflate(reader, clen, unlen, &writer, nullptr) != 0) {
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001313 goto bail;
1314 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001315 break;
Narayan Kamath0e4110e2017-10-26 18:00:13 +01001316 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001317 default:
1318 goto bail;
1319 }
1320 return buf;
1321
1322bail:
1323 free(buf);
1324 return NULL;
1325}
1326
1327
1328/*
1329 * ===========================================================================
1330 * ZipFile::EndOfCentralDir
1331 * ===========================================================================
1332 */
1333
1334/*
1335 * Read the end-of-central-dir fields.
1336 *
1337 * "buf" should be positioned at the EOCD signature, and should contain
1338 * the entire EOCD area including the comment.
1339 */
Dan Willemsen41bc4242015-11-04 14:08:20 -08001340status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001341{
1342 /* don't allow re-use */
1343 assert(mComment == NULL);
1344
1345 if (len < kEOCDLen) {
1346 /* looks like ZIP file got truncated */
Steve Block15fab1a2011-12-20 16:25:43 +00001347 ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001348 kEOCDLen, len);
1349 return INVALID_OPERATION;
1350 }
1351
1352 /* this should probably be an assert() */
1353 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
1354 return UNKNOWN_ERROR;
1355
1356 mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
1357 mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
1358 mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
1359 mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
1360 mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
1361 mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
1362 mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
1363
1364 // TODO: validate mCentralDirOffset
1365
1366 if (mCommentLen > 0) {
1367 if (kEOCDLen + mCommentLen > len) {
Dan Willemsen41bc4242015-11-04 14:08:20 -08001368 ALOGD("EOCD(%d) + comment(%" PRIu16 ") exceeds len (%d)\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001369 kEOCDLen, mCommentLen, len);
1370 return UNKNOWN_ERROR;
1371 }
Dan Willemsen41bc4242015-11-04 14:08:20 -08001372 mComment = new uint8_t[mCommentLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001373 memcpy(mComment, buf + kEOCDLen, mCommentLen);
1374 }
1375
Elliott Hughesad7d5622018-10-08 11:19:28 -07001376 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001377}
1378
1379/*
1380 * Write an end-of-central-directory section.
1381 */
1382status_t ZipFile::EndOfCentralDir::write(FILE* fp)
1383{
Dan Willemsen41bc4242015-11-04 14:08:20 -08001384 uint8_t buf[kEOCDLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001385
1386 ZipEntry::putLongLE(&buf[0x00], kSignature);
1387 ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
1388 ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
1389 ZipEntry::putShortLE(&buf[0x08], mNumEntries);
1390 ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
1391 ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
1392 ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
1393 ZipEntry::putShortLE(&buf[0x14], mCommentLen);
1394
Colin Cross383e4822021-01-08 11:06:24 -08001395 if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) {
1396 ALOGW("fwrite EOCD failed, %s", strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001397 return UNKNOWN_ERROR;
Colin Cross383e4822021-01-08 11:06:24 -08001398 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001399 if (mCommentLen > 0) {
1400 assert(mComment != NULL);
Colin Cross383e4822021-01-08 11:06:24 -08001401 if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) {
1402 ALOGW("fwrite %d bytes failed, %s",
1403 (int) mCommentLen, strerror(errno));
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001404 return UNKNOWN_ERROR;
Colin Cross383e4822021-01-08 11:06:24 -08001405 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001406 }
1407
Elliott Hughesad7d5622018-10-08 11:19:28 -07001408 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001409}
1410
1411/*
1412 * Dump the contents of an EndOfCentralDir object.
1413 */
1414void ZipFile::EndOfCentralDir::dump(void) const
1415{
Steve Block15fab1a2011-12-20 16:25:43 +00001416 ALOGD(" EndOfCentralDir contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -08001417 ALOGD(" diskNum=%" PRIu16 " diskWCD=%" PRIu16 " numEnt=%" PRIu16 " totalNumEnt=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001418 mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
Dan Willemsen41bc4242015-11-04 14:08:20 -08001419 ALOGD(" centDirSize=%" PRIu32 " centDirOff=%" PRIu32 " commentLen=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001420 mCentralDirSize, mCentralDirOffset, mCommentLen);
1421}
1422
Fabien Sanglard0f29f542020-10-22 17:58:12 -07001423} // namespace android