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