xref: /aosp_15_r20/build/make/tools/ziptime/ZipFile.cpp (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker /*
2*9e94795aSAndroid Build Coastguard Worker  * Copyright (C) 2006 The Android Open Source Project
3*9e94795aSAndroid Build Coastguard Worker  *
4*9e94795aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*9e94795aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*9e94795aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*9e94795aSAndroid Build Coastguard Worker  *
8*9e94795aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*9e94795aSAndroid Build Coastguard Worker  *
10*9e94795aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*9e94795aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*9e94795aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9e94795aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*9e94795aSAndroid Build Coastguard Worker  * limitations under the License.
15*9e94795aSAndroid Build Coastguard Worker  */
16*9e94795aSAndroid Build Coastguard Worker 
17*9e94795aSAndroid Build Coastguard Worker //
18*9e94795aSAndroid Build Coastguard Worker // Access to Zip archives.
19*9e94795aSAndroid Build Coastguard Worker //
20*9e94795aSAndroid Build Coastguard Worker 
21*9e94795aSAndroid Build Coastguard Worker #include "ZipFile.h"
22*9e94795aSAndroid Build Coastguard Worker 
23*9e94795aSAndroid Build Coastguard Worker #include <memory.h>
24*9e94795aSAndroid Build Coastguard Worker #include <sys/stat.h>
25*9e94795aSAndroid Build Coastguard Worker #include <errno.h>
26*9e94795aSAndroid Build Coastguard Worker #include <assert.h>
27*9e94795aSAndroid Build Coastguard Worker #include <inttypes.h>
28*9e94795aSAndroid Build Coastguard Worker 
29*9e94795aSAndroid Build Coastguard Worker using namespace android;
30*9e94795aSAndroid Build Coastguard Worker 
31*9e94795aSAndroid Build Coastguard Worker #define LOG(...) fprintf(stderr, __VA_ARGS__)
32*9e94795aSAndroid Build Coastguard Worker 
33*9e94795aSAndroid Build Coastguard Worker /*
34*9e94795aSAndroid Build Coastguard Worker  * Open a file and rewrite the headers
35*9e94795aSAndroid Build Coastguard Worker  */
rewrite(const char * zipFileName)36*9e94795aSAndroid Build Coastguard Worker status_t ZipFile::rewrite(const char* zipFileName)
37*9e94795aSAndroid Build Coastguard Worker {
38*9e94795aSAndroid Build Coastguard Worker     assert(mZipFp == NULL);     // no reopen
39*9e94795aSAndroid Build Coastguard Worker 
40*9e94795aSAndroid Build Coastguard Worker     /* open the file */
41*9e94795aSAndroid Build Coastguard Worker     mZipFp = fopen(zipFileName, "r+b");
42*9e94795aSAndroid Build Coastguard Worker     if (mZipFp == NULL) {
43*9e94795aSAndroid Build Coastguard Worker         LOG("fopen \"%s\" failed: %s\n", zipFileName, strerror(errno));
44*9e94795aSAndroid Build Coastguard Worker         return -1;
45*9e94795aSAndroid Build Coastguard Worker     }
46*9e94795aSAndroid Build Coastguard Worker 
47*9e94795aSAndroid Build Coastguard Worker     /*
48*9e94795aSAndroid Build Coastguard Worker      * Load the central directory.  If that fails, then this probably
49*9e94795aSAndroid Build Coastguard Worker      * isn't a Zip archive.
50*9e94795aSAndroid Build Coastguard Worker      */
51*9e94795aSAndroid Build Coastguard Worker     return rewriteCentralDir();
52*9e94795aSAndroid Build Coastguard Worker }
53*9e94795aSAndroid Build Coastguard Worker 
54*9e94795aSAndroid Build Coastguard Worker /*
55*9e94795aSAndroid Build Coastguard Worker  * Find the central directory, read and rewrite the contents.
56*9e94795aSAndroid Build Coastguard Worker  *
57*9e94795aSAndroid Build Coastguard Worker  * The fun thing about ZIP archives is that they may or may not be
58*9e94795aSAndroid Build Coastguard Worker  * readable from start to end.  In some cases, notably for archives
59*9e94795aSAndroid Build Coastguard Worker  * that were written to stdout, the only length information is in the
60*9e94795aSAndroid Build Coastguard Worker  * central directory at the end of the file.
61*9e94795aSAndroid Build Coastguard Worker  *
62*9e94795aSAndroid Build Coastguard Worker  * Of course, the central directory can be followed by a variable-length
63*9e94795aSAndroid Build Coastguard Worker  * comment field, so we have to scan through it backwards.  The comment
64*9e94795aSAndroid Build Coastguard Worker  * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
65*9e94795aSAndroid Build Coastguard Worker  * itself, plus apparently sometimes people throw random junk on the end
66*9e94795aSAndroid Build Coastguard Worker  * just for the fun of it.
67*9e94795aSAndroid Build Coastguard Worker  *
68*9e94795aSAndroid Build Coastguard Worker  * This is all a little wobbly.  If the wrong value ends up in the EOCD
69*9e94795aSAndroid Build Coastguard Worker  * area, we're hosed.  This appears to be the way that everbody handles
70*9e94795aSAndroid Build Coastguard Worker  * it though, so we're in pretty good company if this fails.
71*9e94795aSAndroid Build Coastguard Worker  */
rewriteCentralDir(void)72*9e94795aSAndroid Build Coastguard Worker status_t ZipFile::rewriteCentralDir(void)
73*9e94795aSAndroid Build Coastguard Worker {
74*9e94795aSAndroid Build Coastguard Worker     fseeko(mZipFp, 0, SEEK_END);
75*9e94795aSAndroid Build Coastguard Worker     off_t fileLength = ftello(mZipFp);
76*9e94795aSAndroid Build Coastguard Worker     rewind(mZipFp);
77*9e94795aSAndroid Build Coastguard Worker 
78*9e94795aSAndroid Build Coastguard Worker     /* too small to be a ZIP archive? */
79*9e94795aSAndroid Build Coastguard Worker     if (fileLength < EndOfCentralDir::kEOCDLen) {
80*9e94795aSAndroid Build Coastguard Worker         LOG("Length is %lld -- too small\n", (long long) fileLength);
81*9e94795aSAndroid Build Coastguard Worker         return -1;
82*9e94795aSAndroid Build Coastguard Worker     }
83*9e94795aSAndroid Build Coastguard Worker 
84*9e94795aSAndroid Build Coastguard Worker     off_t seekStart;
85*9e94795aSAndroid Build Coastguard Worker     size_t readAmount;
86*9e94795aSAndroid Build Coastguard Worker     if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
87*9e94795aSAndroid Build Coastguard Worker         seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
88*9e94795aSAndroid Build Coastguard Worker         readAmount = EndOfCentralDir::kMaxEOCDSearch;
89*9e94795aSAndroid Build Coastguard Worker     } else {
90*9e94795aSAndroid Build Coastguard Worker         seekStart = 0;
91*9e94795aSAndroid Build Coastguard Worker         readAmount = fileLength;
92*9e94795aSAndroid Build Coastguard Worker     }
93*9e94795aSAndroid Build Coastguard Worker     if (fseeko(mZipFp, seekStart, SEEK_SET) != 0) {
94*9e94795aSAndroid Build Coastguard Worker         LOG("Failure seeking to end of zip at %lld", (long long) seekStart);
95*9e94795aSAndroid Build Coastguard Worker         return -1;
96*9e94795aSAndroid Build Coastguard Worker     }
97*9e94795aSAndroid Build Coastguard Worker 
98*9e94795aSAndroid Build Coastguard Worker     /* read the last part of the file into the buffer */
99*9e94795aSAndroid Build Coastguard Worker     uint8_t buf[EndOfCentralDir::kMaxEOCDSearch];
100*9e94795aSAndroid Build Coastguard Worker     if (fread(buf, 1, readAmount, mZipFp) != readAmount) {
101*9e94795aSAndroid Build Coastguard Worker         LOG("short file? wanted %zu\n", readAmount);
102*9e94795aSAndroid Build Coastguard Worker         return -1;
103*9e94795aSAndroid Build Coastguard Worker     }
104*9e94795aSAndroid Build Coastguard Worker 
105*9e94795aSAndroid Build Coastguard Worker     /* find the end-of-central-dir magic */
106*9e94795aSAndroid Build Coastguard Worker     int i;
107*9e94795aSAndroid Build Coastguard Worker     for (i = readAmount - 4; i >= 0; i--) {
108*9e94795aSAndroid Build Coastguard Worker         if (buf[i] == 0x50 &&
109*9e94795aSAndroid Build Coastguard Worker             ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
110*9e94795aSAndroid Build Coastguard Worker         {
111*9e94795aSAndroid Build Coastguard Worker             break;
112*9e94795aSAndroid Build Coastguard Worker         }
113*9e94795aSAndroid Build Coastguard Worker     }
114*9e94795aSAndroid Build Coastguard Worker     if (i < 0) {
115*9e94795aSAndroid Build Coastguard Worker         LOG("EOCD not found, not Zip\n");
116*9e94795aSAndroid Build Coastguard Worker         return -1;
117*9e94795aSAndroid Build Coastguard Worker     }
118*9e94795aSAndroid Build Coastguard Worker 
119*9e94795aSAndroid Build Coastguard Worker     /* extract eocd values */
120*9e94795aSAndroid Build Coastguard Worker     status_t result = mEOCD.readBuf(buf + i, readAmount - i);
121*9e94795aSAndroid Build Coastguard Worker     if (result != 0) {
122*9e94795aSAndroid Build Coastguard Worker         LOG("Failure reading %zu bytes of EOCD values", readAmount - i);
123*9e94795aSAndroid Build Coastguard Worker         return result;
124*9e94795aSAndroid Build Coastguard Worker     }
125*9e94795aSAndroid Build Coastguard Worker 
126*9e94795aSAndroid Build Coastguard Worker     /*
127*9e94795aSAndroid Build Coastguard Worker      * So far so good.  "mCentralDirSize" is the size in bytes of the
128*9e94795aSAndroid Build Coastguard Worker      * central directory, so we can just seek back that far to find it.
129*9e94795aSAndroid Build Coastguard Worker      * We can also seek forward mCentralDirOffset bytes from the
130*9e94795aSAndroid Build Coastguard Worker      * start of the file.
131*9e94795aSAndroid Build Coastguard Worker      *
132*9e94795aSAndroid Build Coastguard Worker      * We're not guaranteed to have the rest of the central dir in the
133*9e94795aSAndroid Build Coastguard Worker      * buffer, nor are we guaranteed that the central dir will have any
134*9e94795aSAndroid Build Coastguard Worker      * sort of convenient size.  We need to skip to the start of it and
135*9e94795aSAndroid Build Coastguard Worker      * read the header, then the other goodies.
136*9e94795aSAndroid Build Coastguard Worker      *
137*9e94795aSAndroid Build Coastguard Worker      * The only thing we really need right now is the file comment, which
138*9e94795aSAndroid Build Coastguard Worker      * we're hoping to preserve.
139*9e94795aSAndroid Build Coastguard Worker      */
140*9e94795aSAndroid Build Coastguard Worker     if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
141*9e94795aSAndroid Build Coastguard Worker         LOG("Failure seeking to central dir offset %" PRIu32 "\n",
142*9e94795aSAndroid Build Coastguard Worker              mEOCD.mCentralDirOffset);
143*9e94795aSAndroid Build Coastguard Worker         return -1;
144*9e94795aSAndroid Build Coastguard Worker     }
145*9e94795aSAndroid Build Coastguard Worker 
146*9e94795aSAndroid Build Coastguard Worker     /*
147*9e94795aSAndroid Build Coastguard Worker      * Loop through and read the central dir entries.
148*9e94795aSAndroid Build Coastguard Worker      */
149*9e94795aSAndroid Build Coastguard Worker     for (int entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
150*9e94795aSAndroid Build Coastguard Worker         ZipEntry* pEntry = new ZipEntry;
151*9e94795aSAndroid Build Coastguard Worker         result = pEntry->initAndRewriteFromCDE(mZipFp);
152*9e94795aSAndroid Build Coastguard Worker         delete pEntry;
153*9e94795aSAndroid Build Coastguard Worker         if (result != 0) {
154*9e94795aSAndroid Build Coastguard Worker             LOG("initFromCDE failed\n");
155*9e94795aSAndroid Build Coastguard Worker             return -1;
156*9e94795aSAndroid Build Coastguard Worker         }
157*9e94795aSAndroid Build Coastguard Worker     }
158*9e94795aSAndroid Build Coastguard Worker 
159*9e94795aSAndroid Build Coastguard Worker     /*
160*9e94795aSAndroid Build Coastguard Worker      * If all went well, we should now be back at the EOCD.
161*9e94795aSAndroid Build Coastguard Worker      */
162*9e94795aSAndroid Build Coastguard Worker     uint8_t checkBuf[4];
163*9e94795aSAndroid Build Coastguard Worker     if (fread(checkBuf, 1, 4, mZipFp) != 4) {
164*9e94795aSAndroid Build Coastguard Worker         LOG("EOCD check read failed\n");
165*9e94795aSAndroid Build Coastguard Worker         return -1;
166*9e94795aSAndroid Build Coastguard Worker     }
167*9e94795aSAndroid Build Coastguard Worker     if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
168*9e94795aSAndroid Build Coastguard Worker         LOG("EOCD read check failed\n");
169*9e94795aSAndroid Build Coastguard Worker         return -1;
170*9e94795aSAndroid Build Coastguard Worker     }
171*9e94795aSAndroid Build Coastguard Worker 
172*9e94795aSAndroid Build Coastguard Worker     return 0;
173*9e94795aSAndroid Build Coastguard Worker }
174*9e94795aSAndroid Build Coastguard Worker 
175*9e94795aSAndroid Build Coastguard Worker /*
176*9e94795aSAndroid Build Coastguard Worker  * ===========================================================================
177*9e94795aSAndroid Build Coastguard Worker  *      ZipFile::EndOfCentralDir
178*9e94795aSAndroid Build Coastguard Worker  * ===========================================================================
179*9e94795aSAndroid Build Coastguard Worker  */
180*9e94795aSAndroid Build Coastguard Worker 
181*9e94795aSAndroid Build Coastguard Worker /*
182*9e94795aSAndroid Build Coastguard Worker  * Read the end-of-central-dir fields.
183*9e94795aSAndroid Build Coastguard Worker  *
184*9e94795aSAndroid Build Coastguard Worker  * "buf" should be positioned at the EOCD signature, and should contain
185*9e94795aSAndroid Build Coastguard Worker  * the entire EOCD area including the comment.
186*9e94795aSAndroid Build Coastguard Worker  */
readBuf(const uint8_t * buf,int len)187*9e94795aSAndroid Build Coastguard Worker status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)
188*9e94795aSAndroid Build Coastguard Worker {
189*9e94795aSAndroid Build Coastguard Worker     uint16_t diskNumber, diskWithCentralDir, numEntries;
190*9e94795aSAndroid Build Coastguard Worker 
191*9e94795aSAndroid Build Coastguard Worker     if (len < kEOCDLen) {
192*9e94795aSAndroid Build Coastguard Worker         /* looks like ZIP file got truncated */
193*9e94795aSAndroid Build Coastguard Worker         LOG(" Zip EOCD: expected >= %d bytes, found %d\n",
194*9e94795aSAndroid Build Coastguard Worker             kEOCDLen, len);
195*9e94795aSAndroid Build Coastguard Worker         return -1;
196*9e94795aSAndroid Build Coastguard Worker     }
197*9e94795aSAndroid Build Coastguard Worker 
198*9e94795aSAndroid Build Coastguard Worker     /* this should probably be an assert() */
199*9e94795aSAndroid Build Coastguard Worker     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
200*9e94795aSAndroid Build Coastguard Worker         return -1;
201*9e94795aSAndroid Build Coastguard Worker 
202*9e94795aSAndroid Build Coastguard Worker     diskNumber = ZipEntry::getShortLE(&buf[0x04]);
203*9e94795aSAndroid Build Coastguard Worker     diskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
204*9e94795aSAndroid Build Coastguard Worker     numEntries = ZipEntry::getShortLE(&buf[0x08]);
205*9e94795aSAndroid Build Coastguard Worker     mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
206*9e94795aSAndroid Build Coastguard Worker     mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
207*9e94795aSAndroid Build Coastguard Worker 
208*9e94795aSAndroid Build Coastguard Worker     if (diskNumber != 0 || diskWithCentralDir != 0 ||
209*9e94795aSAndroid Build Coastguard Worker         numEntries != mTotalNumEntries)
210*9e94795aSAndroid Build Coastguard Worker     {
211*9e94795aSAndroid Build Coastguard Worker         LOG("Archive spanning not supported\n");
212*9e94795aSAndroid Build Coastguard Worker         return -1;
213*9e94795aSAndroid Build Coastguard Worker     }
214*9e94795aSAndroid Build Coastguard Worker 
215*9e94795aSAndroid Build Coastguard Worker     return 0;
216*9e94795aSAndroid Build Coastguard Worker }
217