blob: 689999ee18437d755c080d7dbf45b1aef9ef6be8 [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
21#define LOG_TAG "zip"
22
23#include "ZipEntry.h"
24#include <utils/Log.h>
25
Mathias Agopian3344b2e2009-06-05 14:55:48 -070026#include <assert.h>
Dan Willemsen41bc4242015-11-04 14:08:20 -080027#include <inttypes.h>
Mark Salyzyn404fd5b2016-10-17 10:20:33 -070028#include <stdio.h>
29#include <string.h>
30#include <time.h>
Mathias Agopian3344b2e2009-06-05 14:55:48 -070031
Fabien Sanglard0f29f542020-10-22 17:58:12 -070032namespace android {
Mathias Agopian3344b2e2009-06-05 14:55:48 -070033
34/*
35 * Initialize a new ZipEntry structure from a FILE* positioned at a
36 * CentralDirectoryEntry.
37 *
38 * On exit, the file pointer will be at the start of the next CDE or
39 * at the EOCD.
40 */
41status_t ZipEntry::initFromCDE(FILE* fp)
42{
Steve Block2da72c62011-10-20 11:56:20 +010043 //ALOGV("initFromCDE ---\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070044
45 /* read the CDE */
Elliott Hughes04434732023-01-10 22:59:40 +000046 status_t result = mCDE.read(fp);
Elliott Hughesad7d5622018-10-08 11:19:28 -070047 if (result != OK) {
Steve Block15fab1a2011-12-20 16:25:43 +000048 ALOGD("mCDE.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070049 return result;
50 }
51
52 //mCDE.dump();
53
54 /* using the info in the CDE, go load up the LFH */
Elliott Hughes04434732023-01-10 22:59:40 +000055 off_t posn = ftello(fp);
56 if (fseeko(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -080057 ALOGD("local header seek failed (%" PRIu32 ")\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -070058 mCDE.mLocalHeaderRelOffset);
59 return UNKNOWN_ERROR;
60 }
61
62 result = mLFH.read(fp);
Elliott Hughesad7d5622018-10-08 11:19:28 -070063 if (result != OK) {
Steve Block15fab1a2011-12-20 16:25:43 +000064 ALOGD("mLFH.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070065 return result;
66 }
67
Elliott Hughes04434732023-01-10 22:59:40 +000068 if (fseeko(fp, posn, SEEK_SET) != 0)
Mathias Agopian3344b2e2009-06-05 14:55:48 -070069 return UNKNOWN_ERROR;
70
71 //mLFH.dump();
72
73 /*
74 * We *might* need to read the Data Descriptor at this point and
75 * integrate it into the LFH. If this bit is set, the CRC-32,
76 * compressed size, and uncompressed size will be zero. In practice
77 * these seem to be rare.
78 */
Elliott Hughes04434732023-01-10 22:59:40 +000079 bool hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
Mathias Agopian3344b2e2009-06-05 14:55:48 -070080 if (hasDD) {
81 // do something clever
Steve Block15fab1a2011-12-20 16:25:43 +000082 //ALOGD("+++ has data descriptor\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070083 }
84
85 /*
Mark Chienb19716d2021-02-24 16:37:02 +000086 * Check the LFH. Note that this will fail if the "kUsesDataDescr"
Mathias Agopian3344b2e2009-06-05 14:55:48 -070087 * flag is set, because the LFH is incomplete. (Not a problem, since we
88 * prefer the CDE values.)
89 */
90 if (!hasDD && !compareHeaders()) {
Steve Blockc0b74df2012-01-05 23:28:01 +000091 ALOGW("WARNING: header mismatch\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070092 // keep going?
93 }
94
95 /*
96 * If the mVersionToExtract is greater than 20, we may have an
97 * issue unpacking the record -- could be encrypted, compressed
98 * with something we don't support, or use Zip64 extensions. We
99 * can defer worrying about that to when we're extracting data.
100 */
101
Elliott Hughesad7d5622018-10-08 11:19:28 -0700102 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700103}
104
105/*
106 * Initialize a new entry. Pass in the file name and an optional comment.
107 *
108 * Initializes the CDE and the LFH.
109 */
110void ZipEntry::initNew(const char* fileName, const char* comment)
111{
112 assert(fileName != NULL && *fileName != '\0'); // name required
113
114 /* most fields are properly initialized by constructor */
115 mCDE.mVersionMadeBy = kDefaultMadeBy;
116 mCDE.mVersionToExtract = kDefaultVersion;
117 mCDE.mCompressionMethod = kCompressStored;
118 mCDE.mFileNameLength = strlen(fileName);
119 if (comment != NULL)
120 mCDE.mFileCommentLength = strlen(comment);
121 mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
122
123 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800124 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700125 strcpy((char*) mCDE.mFileName, fileName);
126 }
127 if (mCDE.mFileCommentLength > 0) {
128 /* TODO: stop assuming null-terminated ASCII here? */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800129 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Yunlian Jiang4f1a91c2016-10-04 17:23:03 -0700130 assert(comment != NULL);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700131 strcpy((char*) mCDE.mFileComment, comment);
132 }
133
134 copyCDEtoLFH();
135}
136
137/*
138 * Initialize a new entry, starting with the ZipEntry from a different
139 * archive.
140 *
141 * Initializes the CDE and the LFH.
142 */
Aurimas Liutikasaf1d7412016-02-11 18:11:21 -0800143status_t ZipEntry::initFromExternal(const ZipEntry* pEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700144{
145 /*
146 * Copy everything in the CDE over, then fix up the hairy bits.
147 */
148 memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
149
150 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800151 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700152 if (mCDE.mFileName == NULL)
153 return NO_MEMORY;
154 strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
155 }
156 if (mCDE.mFileCommentLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800157 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700158 if (mCDE.mFileComment == NULL)
159 return NO_MEMORY;
160 strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
161 }
162 if (mCDE.mExtraFieldLength > 0) {
163 /* we null-terminate this, though it may not be a string */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800164 mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700165 if (mCDE.mExtraField == NULL)
166 return NO_MEMORY;
167 memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
168 mCDE.mExtraFieldLength+1);
169 }
170
171 /* construct the LFH from the CDE */
172 copyCDEtoLFH();
173
174 /*
175 * The LFH "extra" field is independent of the CDE "extra", so we
176 * handle it here.
177 */
178 assert(mLFH.mExtraField == NULL);
179 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
180 if (mLFH.mExtraFieldLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800181 mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700182 if (mLFH.mExtraField == NULL)
183 return NO_MEMORY;
184 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
185 mLFH.mExtraFieldLength+1);
186 }
187
Elliott Hughesad7d5622018-10-08 11:19:28 -0700188 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700189}
190
191/*
192 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
193 * potentially confuse something that put "extra" data in here earlier,
194 * but I can't find an actual problem.
195 */
196status_t ZipEntry::addPadding(int padding)
197{
198 if (padding <= 0)
199 return INVALID_OPERATION;
200
Steve Block934443b2012-01-04 20:07:45 +0000201 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700202 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
203
204 if (mLFH.mExtraFieldLength > 0) {
205 /* extend existing field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800206 uint8_t* newExtra;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700207
Dan Willemsen41bc4242015-11-04 14:08:20 -0800208 newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700209 if (newExtra == NULL)
210 return NO_MEMORY;
211 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
212 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
213
214 delete[] mLFH.mExtraField;
215 mLFH.mExtraField = newExtra;
216 mLFH.mExtraFieldLength += padding;
217 } else {
218 /* create new field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800219 mLFH.mExtraField = new uint8_t[padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700220 memset(mLFH.mExtraField, 0, padding);
221 mLFH.mExtraFieldLength = padding;
222 }
223
Elliott Hughesad7d5622018-10-08 11:19:28 -0700224 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700225}
226
227/*
228 * Set the fields in the LFH equal to the corresponding fields in the CDE.
229 *
230 * This does not touch the LFH "extra" field.
231 */
232void ZipEntry::copyCDEtoLFH(void)
233{
234 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
235 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
236 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
237 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
238 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
239 mLFH.mCRC32 = mCDE.mCRC32;
240 mLFH.mCompressedSize = mCDE.mCompressedSize;
241 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
242 mLFH.mFileNameLength = mCDE.mFileNameLength;
243 // the "extra field" is independent
244
245 delete[] mLFH.mFileName;
246 if (mLFH.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800247 mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700248 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
249 } else {
250 mLFH.mFileName = NULL;
251 }
252}
253
254/*
255 * Set some information about a file after we add it.
256 */
Chih-Hung Hsieh0c0d9282018-08-10 15:14:26 -0700257void ZipEntry::setDataInfo(uint32_t uncompLen, uint32_t compLen, uint32_t crc32,
258 uint32_t compressionMethod)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700259{
260 mCDE.mCompressionMethod = compressionMethod;
261 mCDE.mCRC32 = crc32;
262 mCDE.mCompressedSize = compLen;
263 mCDE.mUncompressedSize = uncompLen;
264 mCDE.mCompressionMethod = compressionMethod;
265 if (compressionMethod == kCompressDeflated) {
266 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
267 }
268 copyCDEtoLFH();
269}
270
271/*
272 * See if the data in mCDE and mLFH match up. This is mostly useful for
273 * debugging these classes, but it can be used to identify damaged
274 * archives.
275 *
276 * Returns "false" if they differ.
277 */
278bool ZipEntry::compareHeaders(void) const
279{
280 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
Steve Block2da72c62011-10-20 11:56:20 +0100281 ALOGV("cmp: VersionToExtract\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700282 return false;
283 }
284 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
Steve Block2da72c62011-10-20 11:56:20 +0100285 ALOGV("cmp: GPBitFlag\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700286 return false;
287 }
288 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
Steve Block2da72c62011-10-20 11:56:20 +0100289 ALOGV("cmp: CompressionMethod\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700290 return false;
291 }
292 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
Steve Block2da72c62011-10-20 11:56:20 +0100293 ALOGV("cmp: LastModFileTime\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700294 return false;
295 }
296 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
Steve Block2da72c62011-10-20 11:56:20 +0100297 ALOGV("cmp: LastModFileDate\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700298 return false;
299 }
300 if (mCDE.mCRC32 != mLFH.mCRC32) {
Steve Block2da72c62011-10-20 11:56:20 +0100301 ALOGV("cmp: CRC32\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700302 return false;
303 }
304 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100305 ALOGV("cmp: CompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700306 return false;
307 }
308 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100309 ALOGV("cmp: UncompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700310 return false;
311 }
312 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100313 ALOGV("cmp: FileNameLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700314 return false;
315 }
316#if 0 // this seems to be used for padding, not real data
317 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100318 ALOGV("cmp: ExtraFieldLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700319 return false;
320 }
321#endif
322 if (mCDE.mFileName != NULL) {
323 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
Steve Block2da72c62011-10-20 11:56:20 +0100324 ALOGV("cmp: FileName\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700325 return false;
326 }
327 }
328
329 return true;
330}
331
332
333/*
334 * Convert the DOS date/time stamp into a UNIX time stamp.
335 */
336time_t ZipEntry::getModWhen(void) const
337{
338 struct tm parts;
339
340 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
341 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
342 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
343 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
344 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
345 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
346 parts.tm_wday = parts.tm_yday = 0;
347 parts.tm_isdst = -1; // DST info "not available"
348
349 return mktime(&parts);
350}
351
352/*
353 * Set the CDE/LFH timestamp from UNIX time.
354 */
355void ZipEntry::setModWhen(time_t when)
356{
Yabin Cuie9ae59b2014-11-13 11:51:37 -0800357#if !defined(_WIN32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700358 struct tm tmResult;
359#endif
360 time_t even;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800361 uint16_t zdate, ztime;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700362
363 struct tm* ptm;
364
365 /* round up to an even number of seconds */
Chih-Hung Hsieh0c0d9282018-08-10 15:14:26 -0700366 even = (when & 1) ? (when + 1) : when;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700367
368 /* expand */
Yabin Cuie9ae59b2014-11-13 11:51:37 -0800369#if !defined(_WIN32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700370 ptm = localtime_r(&even, &tmResult);
371#else
372 ptm = localtime(&even);
373#endif
374
375 int year;
376 year = ptm->tm_year;
377 if (year < 80)
378 year = 80;
379
380 zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
381 ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
382
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