diff options
author | 2015-10-29 16:33:05 -0700 | |
---|---|---|
committer | 2015-11-02 14:51:54 -0800 | |
commit | 48a621c2779c74cba3555c96e3fbc7b1989ac90b (patch) | |
tree | aca0a2ea83a88527e418a9c3cf4349fb5a89c451 /tools/ziptime/ZipFile.cpp | |
parent | f589c7b442feb02843bdf82305b599b686fc5204 (diff) |
Remove changing uids/timestamps from zip/jar files
Pass -X to zip so that Unix UID/GID and extra timestamps aren't
saved into the zip files.
Add a new tool, ziptime, that uses a very stripped down copy of
zipalign. It no longer depends on libandroidfw, and now rewrites the
timestamps in place instead of making a copy of the zipfile. This should
improve speed and reduce disk requirements, especially with the large
packaging zip files.
Bug: 24201956
Change-Id: I50f68669f659da1b4393e964ad40b6aafb00c1e7
Diffstat (limited to 'tools/ziptime/ZipFile.cpp')
-rw-r--r-- | tools/ziptime/ZipFile.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/tools/ziptime/ZipFile.cpp b/tools/ziptime/ZipFile.cpp new file mode 100644 index 0000000000..c4c898e9b3 --- /dev/null +++ b/tools/ziptime/ZipFile.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Access to Zip archives. +// + +#include "ZipFile.h" + +#include <memory.h> +#include <sys/stat.h> +#include <errno.h> +#include <assert.h> + +using namespace android; + +#define LOG(...) fprintf(stderr, __VA_ARGS__) + +/* + * Open a file and rewrite the headers + */ +status_t ZipFile::rewrite(const char* zipFileName) +{ + assert(mZipFp == NULL); // no reopen + + /* open the file */ + mZipFp = fopen(zipFileName, "r+b"); + if (mZipFp == NULL) { + int err = errno; + LOG("fopen failed: %d\n", err); + return -1; + } + + /* + * Load the central directory. If that fails, then this probably + * isn't a Zip archive. + */ + return rewriteCentralDir(); +} + +/* + * Find the central directory, read and rewrite the contents. + * + * The fun thing about ZIP archives is that they may or may not be + * readable from start to end. In some cases, notably for archives + * that were written to stdout, the only length information is in the + * central directory at the end of the file. + * + * Of course, the central directory can be followed by a variable-length + * comment field, so we have to scan through it backwards. The comment + * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff + * itself, plus apparently sometimes people throw random junk on the end + * just for the fun of it. + * + * This is all a little wobbly. If the wrong value ends up in the EOCD + * area, we're hosed. This appears to be the way that everbody handles + * it though, so we're in pretty good company if this fails. + */ +status_t ZipFile::rewriteCentralDir(void) +{ + status_t result = 0; + unsigned char* buf = NULL; + off_t fileLength, seekStart; + long readAmount; + int i; + + fseek(mZipFp, 0, SEEK_END); + fileLength = ftell(mZipFp); + rewind(mZipFp); + + /* too small to be a ZIP archive? */ + if (fileLength < EndOfCentralDir::kEOCDLen) { + LOG("Length is %ld -- too small\n", (long)fileLength); + result = -1; + goto bail; + } + + buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; + if (buf == NULL) { + LOG("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); + result = -1; + goto bail; + } + + if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { + seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; + readAmount = EndOfCentralDir::kMaxEOCDSearch; + } else { + seekStart = 0; + readAmount = (long) fileLength; + } + if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { + LOG("Failure seeking to end of zip at %ld", (long) seekStart); + result = -1; + goto bail; + } + + /* read the last part of the file into the buffer */ + if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { + LOG("short file? wanted %ld\n", readAmount); + result = -1; + goto bail; + } + + /* find the end-of-central-dir magic */ + for (i = readAmount - 4; i >= 0; i--) { + if (buf[i] == 0x50 && + ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) + { + break; + } + } + if (i < 0) { + LOG("EOCD not found, not Zip\n"); + result = -1; + goto bail; + } + + /* extract eocd values */ + result = mEOCD.readBuf(buf + i, readAmount - i); + if (result != 0) { + LOG("Failure reading %ld bytes of EOCD values", readAmount - i); + goto bail; + } + + /* + * So far so good. "mCentralDirSize" is the size in bytes of the + * central directory, so we can just seek back that far to find it. + * We can also seek forward mCentralDirOffset bytes from the + * start of the file. + * + * We're not guaranteed to have the rest of the central dir in the + * buffer, nor are we guaranteed that the central dir will have any + * sort of convenient size. We need to skip to the start of it and + * read the header, then the other goodies. + * + * The only thing we really need right now is the file comment, which + * we're hoping to preserve. + */ + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + LOG("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); + result = -1; + goto bail; + } + + /* + * Loop through and read the central dir entries. + */ + int entry; + for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { + ZipEntry* pEntry = new ZipEntry; + + result = pEntry->initAndRewriteFromCDE(mZipFp); + if (result != 0) { + LOG("initFromCDE failed\n"); + delete pEntry; + goto bail; + } + + delete pEntry; + } + + + /* + * If all went well, we should now be back at the EOCD. + */ + unsigned char checkBuf[4]; + if (fread(checkBuf, 1, 4, mZipFp) != 4) { + LOG("EOCD check read failed\n"); + result = -1; + goto bail; + } + if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { + LOG("EOCD read check failed\n"); + result = -1; + goto bail; + } + +bail: + delete[] buf; + return result; +} + +/* + * =========================================================================== + * ZipFile::EndOfCentralDir + * =========================================================================== + */ + +/* + * Read the end-of-central-dir fields. + * + * "buf" should be positioned at the EOCD signature, and should contain + * the entire EOCD area including the comment. + */ +status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) +{ + unsigned short diskNumber, diskWithCentralDir, numEntries; + + if (len < kEOCDLen) { + /* looks like ZIP file got truncated */ + LOG(" Zip EOCD: expected >= %d bytes, found %d\n", + kEOCDLen, len); + return -1; + } + + /* this should probably be an assert() */ + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) + return -1; + + diskNumber = ZipEntry::getShortLE(&buf[0x04]); + diskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); + numEntries = ZipEntry::getShortLE(&buf[0x08]); + mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); + mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); + + if (diskNumber != 0 || diskWithCentralDir != 0 || + numEntries != mTotalNumEntries) + { + LOG("Archive spanning not supported\n"); + return -1; + } + + return 0; +} |