blob: dd2eac60a85ae947f1861e486222ddd8ca8c5bb3 [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
366 int year;
367 year = ptm->tm_year;
368 if (year < 80)
369 year = 80;
370
Elliott Hughes16caa442023-09-05 21:37:01 +0000371 uint16_t zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
372 uint16_t ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700373
374 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
375 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
376}
377
378
379/*
380 * ===========================================================================
381 * ZipEntry::LocalFileHeader
382 * ===========================================================================
383 */
384
385/*
386 * Read a local file header.
387 *
388 * On entry, "fp" points to the signature at the start of the header.
389 * On exit, "fp" points to the start of data.
390 */
391status_t ZipEntry::LocalFileHeader::read(FILE* fp)
392{
Elliott Hughesad7d5622018-10-08 11:19:28 -0700393 status_t result = OK;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800394 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700395
396 assert(mFileName == NULL);
397 assert(mExtraField == NULL);
398
399 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
400 result = UNKNOWN_ERROR;
401 goto bail;
402 }
403
404 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000405 ALOGD("whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700406 result = UNKNOWN_ERROR;
407 goto bail;
408 }
409
410 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
411 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
412 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
413 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
414 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
415 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
416 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
417 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
418 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
419 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
420
421 // TODO: validate sizes
422
423 /* grab filename */
424 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800425 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700426 if (mFileName == NULL) {
427 result = NO_MEMORY;
428 goto bail;
429 }
430 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
431 result = UNKNOWN_ERROR;
432 goto bail;
433 }
434 mFileName[mFileNameLength] = '\0';
435 }
436
437 /* grab extra field */
438 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800439 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700440 if (mExtraField == NULL) {
441 result = NO_MEMORY;
442 goto bail;
443 }
444 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
445 result = UNKNOWN_ERROR;
446 goto bail;
447 }
448 mExtraField[mExtraFieldLength] = '\0';
449 }
450
451bail:
452 return result;
453}
454
455/*
456 * Write a local file header.
457 */
458status_t ZipEntry::LocalFileHeader::write(FILE* fp)
459{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800460 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700461
462 ZipEntry::putLongLE(&buf[0x00], kSignature);
463 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
464 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
465 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
466 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
467 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
468 ZipEntry::putLongLE(&buf[0x0e], mCRC32);
469 ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
470 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
471 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
472 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
473
474 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
475 return UNKNOWN_ERROR;
476
477 /* write filename */
478 if (mFileNameLength != 0) {
479 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
480 return UNKNOWN_ERROR;
481 }
482
483 /* write "extra field" */
484 if (mExtraFieldLength != 0) {
485 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
486 return UNKNOWN_ERROR;
487 }
488
Elliott Hughesad7d5622018-10-08 11:19:28 -0700489 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700490}
491
492
493/*
494 * Dump the contents of a LocalFileHeader object.
495 */
496void ZipEntry::LocalFileHeader::dump(void) const
497{
Steve Block15fab1a2011-12-20 16:25:43 +0000498 ALOGD(" LocalFileHeader contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800499 ALOGD(" versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700500 mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800501 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700502 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800503 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700504 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800505 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700506 mFileNameLength, mExtraFieldLength);
507 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000508 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700509}
510
511
512/*
513 * ===========================================================================
514 * ZipEntry::CentralDirEntry
515 * ===========================================================================
516 */
517
518/*
519 * Read the central dir entry that appears next in the file.
520 *
521 * On entry, "fp" should be positioned on the signature bytes for the
522 * entry. On exit, "fp" will point at the signature word for the next
523 * entry or for the EOCD.
524 */
525status_t ZipEntry::CentralDirEntry::read(FILE* fp)
526{
Elliott Hughesad7d5622018-10-08 11:19:28 -0700527 status_t result = OK;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800528 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700529
530 /* no re-use */
531 assert(mFileName == NULL);
532 assert(mExtraField == NULL);
533 assert(mFileComment == NULL);
534
535 if (fread(buf, 1, kCDELen, fp) != kCDELen) {
536 result = UNKNOWN_ERROR;
537 goto bail;
538 }
539
540 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000541 ALOGD("Whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700542 result = UNKNOWN_ERROR;
543 goto bail;
544 }
545
546 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
547 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
548 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
549 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
550 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
551 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
552 mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
553 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
554 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
555 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
556 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
557 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
558 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
559 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
560 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
561 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
562
563 // TODO: validate sizes and offsets
564
565 /* grab filename */
566 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800567 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700568 if (mFileName == NULL) {
569 result = NO_MEMORY;
570 goto bail;
571 }
572 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
573 result = UNKNOWN_ERROR;
574 goto bail;
575 }
576 mFileName[mFileNameLength] = '\0';
577 }
578
579 /* read "extra field" */
580 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800581 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700582 if (mExtraField == NULL) {
583 result = NO_MEMORY;
584 goto bail;
585 }
586 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
587 result = UNKNOWN_ERROR;
588 goto bail;
589 }
590 mExtraField[mExtraFieldLength] = '\0';
591 }
592
593
594 /* grab comment, if any */
595 if (mFileCommentLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800596 mFileComment = new uint8_t[mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700597 if (mFileComment == NULL) {
598 result = NO_MEMORY;
599 goto bail;
600 }
601 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
602 {
603 result = UNKNOWN_ERROR;
604 goto bail;
605 }
606 mFileComment[mFileCommentLength] = '\0';
607 }
608
609bail:
610 return result;
611}
612
613/*
614 * Write a central dir entry.
615 */
616status_t ZipEntry::CentralDirEntry::write(FILE* fp)
617{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800618 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700619
620 ZipEntry::putLongLE(&buf[0x00], kSignature);
621 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
622 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
623 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
624 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
625 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
626 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
627 ZipEntry::putLongLE(&buf[0x10], mCRC32);
628 ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
629 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
630 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
631 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
632 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
633 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
634 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
635 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
636 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
637
638 if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
639 return UNKNOWN_ERROR;
640
641 /* write filename */
642 if (mFileNameLength != 0) {
643 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
644 return UNKNOWN_ERROR;
645 }
646
647 /* write "extra field" */
648 if (mExtraFieldLength != 0) {
649 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
650 return UNKNOWN_ERROR;
651 }
652
653 /* write comment */
654 if (mFileCommentLength != 0) {
655 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
656 return UNKNOWN_ERROR;
657 }
658
Elliott Hughesad7d5622018-10-08 11:19:28 -0700659 return OK;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700660}
661
662/*
663 * Dump the contents of a CentralDirEntry object.
664 */
665void ZipEntry::CentralDirEntry::dump(void) const
666{
Steve Block15fab1a2011-12-20 16:25:43 +0000667 ALOGD(" CentralDirEntry contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800668 ALOGD(" versMadeBy=%" PRIu16 " versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700669 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800670 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700671 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800672 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700673 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800674 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 " commentLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700675 mFileNameLength, mExtraFieldLength, mFileCommentLength);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800676 ALOGD(" diskNumStart=%" PRIu16 " intAttr=0x%04" PRIx16 " extAttr=0x%08" PRIx32 " relOffset=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700677 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
678 mLocalHeaderRelOffset);
679
680 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000681 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700682 if (mFileComment != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000683 ALOGD(" comment: '%s'\n", mFileComment);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700684}
685
Fabien Sanglard0f29f542020-10-22 17:58:12 -0700686} // namespace android
687