blob: 0355e5623827b365dc2dbcc4332ef1f060e65b9e [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 entries in a Zip archive.
19//
20
Elliott Hughes16caa442023-09-05 21:37:01 +000021#define _POSIX_THREAD_SAFE_FUNCTIONS // For mingw localtime_r().
22
Mathias Agopian3344b2e2009-06-05 14:55:48 -070023#define LOG_TAG "zip"
24
25#include "ZipEntry.h"
26#include <utils/Log.h>
27
Mathias Agopian3344b2e2009-06-05 14:55:48 -070028#include <assert.h>
Dan Willemsen41bc4242015-11-04 14:08:20 -080029#include <inttypes.h>
Mark Salyzyn404fd5b2016-10-17 10:20:33 -070030#include <stdio.h>
31#include <string.h>
32#include <time.h>
Mathias Agopian3344b2e2009-06-05 14:55:48 -070033
Fabien Sanglard0f29f542020-10-22 17:58:12 -070034namespace android {
Mathias Agopian3344b2e2009-06-05 14:55:48 -070035
36/*
37 * Initialize a new ZipEntry structure from a FILE* positioned at a
38 * CentralDirectoryEntry.
39 *
40 * On exit, the file pointer will be at the start of the next CDE or
41 * at the EOCD.
42 */
43status_t ZipEntry::initFromCDE(FILE* fp)
44{
Steve Block2da72c62011-10-20 11:56:20 +010045 //ALOGV("initFromCDE ---\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070046
47 /* read the CDE */
Elliott Hughes04434732023-01-10 22:59:40 +000048 status_t result = mCDE.read(fp);
Elliott Hughesad7d5622018-10-08 11:19:28 -070049 if (result != OK) {
Steve Block15fab1a2011-12-20 16:25:43 +000050 ALOGD("mCDE.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070051 return result;
52 }
53
54 //mCDE.dump();
55
56 /* using the info in the CDE, go load up the LFH */
Elliott Hughes04434732023-01-10 22:59:40 +000057 off_t posn = ftello(fp);
58 if (fseeko(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -080059 ALOGD("local header seek failed (%" PRIu32 ")\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -070060 mCDE.mLocalHeaderRelOffset);
61 return UNKNOWN_ERROR;
62 }
63
64 result = mLFH.read(fp);
Elliott Hughesad7d5622018-10-08 11:19:28 -070065 if (result != OK) {
Steve Block15fab1a2011-12-20 16:25:43 +000066 ALOGD("mLFH.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070067 return result;
68 }
69
Elliott Hughes04434732023-01-10 22:59:40 +000070 if (fseeko(fp, posn, SEEK_SET) != 0)
Mathias Agopian3344b2e2009-06-05 14:55:48 -070071 return UNKNOWN_ERROR;
72
73 //mLFH.dump();
74
75 /*
76 * We *might* need to read the Data Descriptor at this point and
77 * integrate it into the LFH. If this bit is set, the CRC-32,
78 * compressed size, and uncompressed size will be zero. In practice
79 * these seem to be rare.
80 */
Elliott Hughes04434732023-01-10 22:59:40 +000081 bool hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
Mathias Agopian3344b2e2009-06-05 14:55:48 -070082 if (hasDD) {
83 // do something clever
Steve Block15fab1a2011-12-20 16:25:43 +000084 //ALOGD("+++ has data descriptor\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070085 }
86
87 /*
Mark Chienb19716d2021-02-24 16:37:02 +000088 * Check the LFH. Note that this will fail if the "kUsesDataDescr"
Mathias Agopian3344b2e2009-06-05 14:55:48 -070089 * flag is set, because the LFH is incomplete. (Not a problem, since we
90 * prefer the CDE values.)
91 */
92 if (!hasDD && !compareHeaders()) {
Steve Blockc0b74df2012-01-05 23:28:01 +000093 ALOGW("WARNING: header mismatch\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070094 // keep going?
95 }
96
97 /*
98 * If the mVersionToExtract is greater than 20, we may have an
99 * issue unpacking the record -- could be encrypted, compressed
100 * with something we don't support, or use Zip64 extensions. We
101 * can defer worrying about that to when we're extracting data.
102 */
103
Elliott Hughesad7d5622018-10-08 11:19:28 -0700104 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700105}
106
107/*
108 * Initialize a new entry. Pass in the file name and an optional comment.
109 *
110 * Initializes the CDE and the LFH.
111 */
112void ZipEntry::initNew(const char* fileName, const char* comment)
113{
114 assert(fileName != NULL && *fileName != '\0'); // name required
115
116 /* most fields are properly initialized by constructor */
117 mCDE.mVersionMadeBy = kDefaultMadeBy;
118 mCDE.mVersionToExtract = kDefaultVersion;
119 mCDE.mCompressionMethod = kCompressStored;
120 mCDE.mFileNameLength = strlen(fileName);
121 if (comment != NULL)
122 mCDE.mFileCommentLength = strlen(comment);
123 mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
124
125 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800126 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700127 strcpy((char*) mCDE.mFileName, fileName);
128 }
129 if (mCDE.mFileCommentLength > 0) {
130 /* TODO: stop assuming null-terminated ASCII here? */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800131 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Yunlian Jiang4f1a91c2016-10-04 17:23:03 -0700132 assert(comment != NULL);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700133 strcpy((char*) mCDE.mFileComment, comment);
134 }
135
136 copyCDEtoLFH();
137}
138
139/*
140 * Initialize a new entry, starting with the ZipEntry from a different
141 * archive.
142 *
143 * Initializes the CDE and the LFH.
144 */
Aurimas Liutikasaf1d7412016-02-11 18:11:21 -0800145status_t ZipEntry::initFromExternal(const ZipEntry* pEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700146{
147 /*
148 * Copy everything in the CDE over, then fix up the hairy bits.
149 */
150 memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
151
152 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800153 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700154 if (mCDE.mFileName == NULL)
155 return NO_MEMORY;
156 strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
157 }
158 if (mCDE.mFileCommentLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800159 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700160 if (mCDE.mFileComment == NULL)
161 return NO_MEMORY;
162 strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
163 }
164 if (mCDE.mExtraFieldLength > 0) {
165 /* we null-terminate this, though it may not be a string */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800166 mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700167 if (mCDE.mExtraField == NULL)
168 return NO_MEMORY;
169 memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
170 mCDE.mExtraFieldLength+1);
171 }
172
173 /* construct the LFH from the CDE */
174 copyCDEtoLFH();
175
176 /*
177 * The LFH "extra" field is independent of the CDE "extra", so we
178 * handle it here.
179 */
180 assert(mLFH.mExtraField == NULL);
181 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
182 if (mLFH.mExtraFieldLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800183 mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700184 if (mLFH.mExtraField == NULL)
185 return NO_MEMORY;
186 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
187 mLFH.mExtraFieldLength+1);
188 }
189
Elliott Hughesad7d5622018-10-08 11:19:28 -0700190 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700191}
192
193/*
194 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
195 * potentially confuse something that put "extra" data in here earlier,
196 * but I can't find an actual problem.
197 */
198status_t ZipEntry::addPadding(int padding)
199{
200 if (padding <= 0)
201 return INVALID_OPERATION;
202
Steve Block934443b2012-01-04 20:07:45 +0000203 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700204 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
205
206 if (mLFH.mExtraFieldLength > 0) {
207 /* extend existing field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800208 uint8_t* newExtra;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700209
Dan Willemsen41bc4242015-11-04 14:08:20 -0800210 newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700211 if (newExtra == NULL)
212 return NO_MEMORY;
213 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
214 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
215
216 delete[] mLFH.mExtraField;
217 mLFH.mExtraField = newExtra;
218 mLFH.mExtraFieldLength += padding;
219 } else {
220 /* create new field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800221 mLFH.mExtraField = new uint8_t[padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700222 memset(mLFH.mExtraField, 0, padding);
223 mLFH.mExtraFieldLength = padding;
224 }
225
Elliott Hughesad7d5622018-10-08 11:19:28 -0700226 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700227}
228
229/*
230 * Set the fields in the LFH equal to the corresponding fields in the CDE.
231 *
232 * This does not touch the LFH "extra" field.
233 */
234void ZipEntry::copyCDEtoLFH(void)
235{
236 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
237 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
238 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
239 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
240 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
241 mLFH.mCRC32 = mCDE.mCRC32;
242 mLFH.mCompressedSize = mCDE.mCompressedSize;
243 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
244 mLFH.mFileNameLength = mCDE.mFileNameLength;
245 // the "extra field" is independent
246
247 delete[] mLFH.mFileName;
248 if (mLFH.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800249 mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700250 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
251 } else {
252 mLFH.mFileName = NULL;
253 }
254}
255
256/*
257 * Set some information about a file after we add it.
258 */
Chih-Hung Hsieh0c0d9282018-08-10 15:14:26 -0700259void ZipEntry::setDataInfo(uint32_t uncompLen, uint32_t compLen, uint32_t crc32,
260 uint32_t compressionMethod)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700261{
262 mCDE.mCompressionMethod = compressionMethod;
263 mCDE.mCRC32 = crc32;
264 mCDE.mCompressedSize = compLen;
265 mCDE.mUncompressedSize = uncompLen;
266 mCDE.mCompressionMethod = compressionMethod;
267 if (compressionMethod == kCompressDeflated) {
268 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
269 }
270 copyCDEtoLFH();
271}
272
273/*
274 * See if the data in mCDE and mLFH match up. This is mostly useful for
275 * debugging these classes, but it can be used to identify damaged
276 * archives.
277 *
278 * Returns "false" if they differ.
279 */
280bool ZipEntry::compareHeaders(void) const
281{
282 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
Steve Block2da72c62011-10-20 11:56:20 +0100283 ALOGV("cmp: VersionToExtract\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700284 return false;
285 }
286 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
Steve Block2da72c62011-10-20 11:56:20 +0100287 ALOGV("cmp: GPBitFlag\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700288 return false;
289 }
290 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
Steve Block2da72c62011-10-20 11:56:20 +0100291 ALOGV("cmp: CompressionMethod\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700292 return false;
293 }
294 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
Steve Block2da72c62011-10-20 11:56:20 +0100295 ALOGV("cmp: LastModFileTime\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700296 return false;
297 }
298 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
Steve Block2da72c62011-10-20 11:56:20 +0100299 ALOGV("cmp: LastModFileDate\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700300 return false;
301 }
302 if (mCDE.mCRC32 != mLFH.mCRC32) {
Steve Block2da72c62011-10-20 11:56:20 +0100303 ALOGV("cmp: CRC32\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700304 return false;
305 }
306 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100307 ALOGV("cmp: CompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700308 return false;
309 }
310 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100311 ALOGV("cmp: UncompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700312 return false;
313 }
314 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100315 ALOGV("cmp: FileNameLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700316 return false;
317 }
318#if 0 // this seems to be used for padding, not real data
319 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100320 ALOGV("cmp: ExtraFieldLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700321 return false;
322 }
323#endif
324 if (mCDE.mFileName != NULL) {
325 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
Steve Block2da72c62011-10-20 11:56:20 +0100326 ALOGV("cmp: FileName\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700327 return false;
328 }
329 }
330
331 return true;
332}
333
334
335/*
336 * Convert the DOS date/time stamp into a UNIX time stamp.
337 */
338time_t ZipEntry::getModWhen(void) const
339{
340 struct tm parts;
341
342 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
343 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
344 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
345 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
346 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
347 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
348 parts.tm_wday = parts.tm_yday = 0;
349 parts.tm_isdst = -1; // DST info "not available"
350
351 return mktime(&parts);
352}
353
354/*
355 * Set the CDE/LFH timestamp from UNIX time.
356 */
357void ZipEntry::setModWhen(time_t when)
358{
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700359 /* round up to an even number of seconds */
Elliott Hughes16caa442023-09-05 21:37:01 +0000360 time_t even = (when & 1) ? (when + 1) : when;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700361
362 /* expand */
Elliott Hughes16caa442023-09-05 21:37:01 +0000363 struct tm tmResult;
364 struct tm* ptm = localtime_r(&even, &tmResult);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700365
Mark Punzalan0d7190b2023-09-06 21:32:07 +0000366 // The earliest valid time for ZIP file entries is 1980-01-01. See:
367 // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html.
368 // Set any time before 1980 to 1980-01-01.
369 if (ptm->tm_year < 80) {
370 ptm->tm_year = 80;
371 ptm->tm_mon = 0;
372 ptm->tm_mday = 1;
373 ptm->tm_hour = 0;
374 ptm->tm_min = 0;
375 ptm->tm_sec = 0;
376 }
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700377
Mark Punzalan0d7190b2023-09-06 21:32:07 +0000378 uint16_t zdate = static_cast<uint16_t>(
379 (ptm->tm_year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
380 uint16_t ztime = static_cast<uint16_t>(
381 ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700382
383 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
384 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
385}
386
387
388/*
389 * ===========================================================================
390 * ZipEntry::LocalFileHeader
391 * ===========================================================================
392 */
393
394/*
395 * Read a local file header.
396 *
397 * On entry, "fp" points to the signature at the start of the header.
398 * On exit, "fp" points to the start of data.
399 */
400status_t ZipEntry::LocalFileHeader::read(FILE* fp)
401{
Elliott Hughesad7d5622018-10-08 11:19:28 -0700402 status_t result = OK;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800403 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700404
405 assert(mFileName == NULL);
406 assert(mExtraField == NULL);
407
408 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
409 result = UNKNOWN_ERROR;
410 goto bail;
411 }
412
413 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000414 ALOGD("whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700415 result = UNKNOWN_ERROR;
416 goto bail;
417 }
418
419 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
420 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
421 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
422 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
423 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
424 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
425 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
426 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
427 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
428 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
429
430 // TODO: validate sizes
431
432 /* grab filename */
433 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800434 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700435 if (mFileName == NULL) {
436 result = NO_MEMORY;
437 goto bail;
438 }
439 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
440 result = UNKNOWN_ERROR;
441 goto bail;
442 }
443 mFileName[mFileNameLength] = '\0';
444 }
445
446 /* grab extra field */
447 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800448 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700449 if (mExtraField == NULL) {
450 result = NO_MEMORY;
451 goto bail;
452 }
453 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
454 result = UNKNOWN_ERROR;
455 goto bail;
456 }
457 mExtraField[mExtraFieldLength] = '\0';
458 }
459
460bail:
461 return result;
462}
463
464/*
465 * Write a local file header.
466 */
467status_t ZipEntry::LocalFileHeader::write(FILE* fp)
468{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800469 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700470
471 ZipEntry::putLongLE(&buf[0x00], kSignature);
472 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
473 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
474 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
475 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
476 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
477 ZipEntry::putLongLE(&buf[0x0e], mCRC32);
478 ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
479 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
480 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
481 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
482
483 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
484 return UNKNOWN_ERROR;
485
486 /* write filename */
487 if (mFileNameLength != 0) {
488 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
489 return UNKNOWN_ERROR;
490 }
491
492 /* write "extra field" */
493 if (mExtraFieldLength != 0) {
494 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
495 return UNKNOWN_ERROR;
496 }
497
Elliott Hughesad7d5622018-10-08 11:19:28 -0700498 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700499}
500
501
502/*
503 * Dump the contents of a LocalFileHeader object.
504 */
505void ZipEntry::LocalFileHeader::dump(void) const
506{
Steve Block15fab1a2011-12-20 16:25:43 +0000507 ALOGD(" LocalFileHeader contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800508 ALOGD(" versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700509 mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800510 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700511 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800512 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700513 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800514 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700515 mFileNameLength, mExtraFieldLength);
516 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000517 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700518}
519
520
521/*
522 * ===========================================================================
523 * ZipEntry::CentralDirEntry
524 * ===========================================================================
525 */
526
527/*
528 * Read the central dir entry that appears next in the file.
529 *
530 * On entry, "fp" should be positioned on the signature bytes for the
531 * entry. On exit, "fp" will point at the signature word for the next
532 * entry or for the EOCD.
533 */
534status_t ZipEntry::CentralDirEntry::read(FILE* fp)
535{
Elliott Hughesad7d5622018-10-08 11:19:28 -0700536 status_t result = OK;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800537 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700538
539 /* no re-use */
540 assert(mFileName == NULL);
541 assert(mExtraField == NULL);
542 assert(mFileComment == NULL);
543
544 if (fread(buf, 1, kCDELen, fp) != kCDELen) {
545 result = UNKNOWN_ERROR;
546 goto bail;
547 }
548
549 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000550 ALOGD("Whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700551 result = UNKNOWN_ERROR;
552 goto bail;
553 }
554
555 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
556 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
557 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
558 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
559 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
560 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
561 mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
562 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
563 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
564 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
565 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
566 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
567 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
568 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
569 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
570 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
571
572 // TODO: validate sizes and offsets
573
574 /* grab filename */
575 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800576 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700577 if (mFileName == NULL) {
578 result = NO_MEMORY;
579 goto bail;
580 }
581 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
582 result = UNKNOWN_ERROR;
583 goto bail;
584 }
585 mFileName[mFileNameLength] = '\0';
586 }
587
588 /* read "extra field" */
589 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800590 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700591 if (mExtraField == NULL) {
592 result = NO_MEMORY;
593 goto bail;
594 }
595 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
596 result = UNKNOWN_ERROR;
597 goto bail;
598 }
599 mExtraField[mExtraFieldLength] = '\0';
600 }
601
602
603 /* grab comment, if any */
604 if (mFileCommentLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800605 mFileComment = new uint8_t[mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700606 if (mFileComment == NULL) {
607 result = NO_MEMORY;
608 goto bail;
609 }
610 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
611 {
612 result = UNKNOWN_ERROR;
613 goto bail;
614 }
615 mFileComment[mFileCommentLength] = '\0';
616 }
617
618bail:
619 return result;
620}
621
622/*
623 * Write a central dir entry.
624 */
625status_t ZipEntry::CentralDirEntry::write(FILE* fp)
626{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800627 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700628
629 ZipEntry::putLongLE(&buf[0x00], kSignature);
630 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
631 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
632 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
633 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
634 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
635 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
636 ZipEntry::putLongLE(&buf[0x10], mCRC32);
637 ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
638 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
639 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
640 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
641 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
642 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
643 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
644 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
645 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
646
647 if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
648 return UNKNOWN_ERROR;
649
650 /* write filename */
651 if (mFileNameLength != 0) {
652 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
653 return UNKNOWN_ERROR;
654 }
655
656 /* write "extra field" */
657 if (mExtraFieldLength != 0) {
658 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
659 return UNKNOWN_ERROR;
660 }
661
662 /* write comment */
663 if (mFileCommentLength != 0) {
664 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
665 return UNKNOWN_ERROR;
666 }
667
Elliott Hughesad7d5622018-10-08 11:19:28 -0700668 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700669}
670
671/*
672 * Dump the contents of a CentralDirEntry object.
673 */
674void ZipEntry::CentralDirEntry::dump(void) const
675{
Steve Block15fab1a2011-12-20 16:25:43 +0000676 ALOGD(" CentralDirEntry contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800677 ALOGD(" versMadeBy=%" PRIu16 " versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700678 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800679 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700680 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800681 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700682 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800683 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 " commentLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700684 mFileNameLength, mExtraFieldLength, mFileCommentLength);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800685 ALOGD(" diskNumStart=%" PRIu16 " intAttr=0x%04" PRIx16 " extAttr=0x%08" PRIx32 " relOffset=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700686 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
687 mLocalHeaderRelOffset);
688
689 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000690 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700691 if (mFileComment != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000692 ALOGD(" comment: '%s'\n", mFileComment);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700693}
694
Fabien Sanglard0f29f542020-10-22 17:58:12 -0700695} // namespace android
696