RawImage MosDecoder::decodeRawInternal() { vector<TiffIFD*> data; TiffIFD* raw = NULL; uint32 off = 0; uint32 base = 8; // We get a pointer up to the end of the file as we check offset bounds later const uchar8 *insideTiff = mFile->getData(base, mFile->getSize()-base); if (get4LE(insideTiff, 0) == 0x49494949) { uint32 offset = get4LE(insideTiff, 8); if (offset+base+4 > mFile->getSize()) ThrowRDE("MOS: PhaseOneC offset out of bounds"); uint32 entries = get4LE(insideTiff, offset); uint32 pos = 8; // Skip another 4 bytes uint32 width=0, height=0, strip_offset=0, data_offset=0, wb_offset=0; while (entries--) { if (offset+base+pos+16 > mFile->getSize()) ThrowRDE("MOS: PhaseOneC offset out of bounds"); uint32 tag = get4LE(insideTiff, offset+pos); //uint32 type = get4LE(insideTiff, offset+pos+4); //uint32 len = get4LE(insideTiff, offset+pos+8); uint32 data = get4LE(insideTiff, offset+pos+12); pos += 16; switch(tag) { case 0x107: wb_offset = data+base; break; case 0x108: width = data; break; case 0x109: height = data; break; case 0x10f: data_offset = data+base; break; case 0x21c: strip_offset = data+base; break; case 0x21d: black_level = data>>2; break; } } if (width <= 0 || height <= 0) ThrowRDE("MOS: PhaseOneC couldn't find width and height"); if (strip_offset+height*4 > mFile->getSize()) ThrowRDE("MOS: PhaseOneC strip offsets out of bounds"); if (data_offset > mFile->getSize()) ThrowRDE("MOS: PhaseOneC data offset out of bounds"); mRaw->dim = iPoint2D(width, height); mRaw->createData(); DecodePhaseOneC(data_offset, strip_offset, width, height); const uchar8 *data = mFile->getData(wb_offset, 12); for(int i=0; i<3; i++) { // Use get4LE instead of going straight to float so this is endian clean uint32 value = get4LE(data, i*4); mRaw->metadata.wbCoeffs[i] = *((float *) &value); } return mRaw; } else {
/** * Convert a binary log entry to ASCII form. * * For convenience we mimic the processLogBuffer API. There is no * pre-defined output length for the binary data, since we're free to format * it however we choose, which means we can't really use a fixed-size buffer * here. */ int android_log_processBinaryLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf, int messageBufLen) { size_t inCount; unsigned int tagIndex; const unsigned char* eventData; entry->tv_sec = buf->sec; entry->tv_nsec = buf->nsec; entry->priority = ANDROID_LOG_INFO; entry->pid = buf->pid; entry->tid = buf->tid; /* * Pull the tag out. */ eventData = (const unsigned char*) buf->msg; inCount = buf->len; if (inCount < 4) return -1; tagIndex = get4LE(eventData); eventData += 4; inCount -= 4; if (map != NULL) { entry->tag = android_lookupEventTag(map, tagIndex); } else { entry->tag = NULL; } /* * If we don't have a map, or didn't find the tag number in the map, * stuff a generated tag value into the start of the output buffer and * shift the buffer pointers down. */ if (entry->tag == NULL) { int tagLen; tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); entry->tag = messageBuf; messageBuf += tagLen+1; messageBufLen -= tagLen+1; } /* * Format the event log data into the buffer. */ char* outBuf = messageBuf; size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ int result; result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining); if (result < 0) { fprintf(stderr, "Binary log entry conversion failed\n"); return -1; } else if (result == 1) { if (outBuf > messageBuf) { /* leave an indicator */ *(outBuf-1) = '!'; } else { /* no room to output anything at all */ *outBuf++ = '!'; outRemaining--; } /* pretend we ate all the data */ inCount = 0; } /* eat the silly terminating '\n' */ if (inCount == 1 && *eventData == '\n') { eventData++; inCount--; } if (inCount != 0) { fprintf(stderr, "Warning: leftover binary log data (%zu bytes)\n", inCount); } /* * Terminate the buffer. The NUL byte does not count as part of * entry->messageLen. */ *outBuf = '\0'; entry->messageLen = outBuf - messageBuf; assert(entry->messageLen == (messageBufLen-1) - outRemaining); entry->message = messageBuf; return 0; }
/* * Recursively convert binary log data to printable form. * * This needs to be recursive because you can have lists of lists. * * If we run out of room, we stop processing immediately. It's important * for us to check for space on every output element to avoid producing * garbled output. * * Returns 0 on success, 1 on buffer full, -1 on failure. */ static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen) { const unsigned char* eventData = *pEventData; size_t eventDataLen = *pEventDataLen; char* outBuf = *pOutBuf; size_t outBufLen = *pOutBufLen; unsigned char type; size_t outCount; int result = 0; if (eventDataLen < 1) return -1; type = *eventData++; eventDataLen--; //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); switch (type) { case EVENT_TYPE_INT: /* 32-bit signed int */ { int ival; if (eventDataLen < 4) return -1; ival = get4LE(eventData); eventData += 4; eventDataLen -= 4; outCount = snprintf(outBuf, outBufLen, "%d", ival); if (outCount < outBufLen) { outBuf += outCount; outBufLen -= outCount; } else { /* halt output */ goto no_room; } } break; case EVENT_TYPE_LONG: /* 64-bit signed long */ { long long lval; if (eventDataLen < 8) return -1; lval = get8LE(eventData); eventData += 8; eventDataLen -= 8; outCount = snprintf(outBuf, outBufLen, "%lld", lval); if (outCount < outBufLen) { outBuf += outCount; outBufLen -= outCount; } else { /* halt output */ goto no_room; } } break; case EVENT_TYPE_STRING: /* UTF-8 chars, not NULL-terminated */ { unsigned int strLen; if (eventDataLen < 4) return -1; strLen = get4LE(eventData); eventData += 4; eventDataLen -= 4; if (eventDataLen < strLen) return -1; if (strLen < outBufLen) { memcpy(outBuf, eventData, strLen); outBuf += strLen; outBufLen -= strLen; } else if (outBufLen > 0) { /* copy what we can */ memcpy(outBuf, eventData, outBufLen); outBuf += outBufLen; outBufLen -= outBufLen; goto no_room; } eventData += strLen; eventDataLen -= strLen; break; } case EVENT_TYPE_LIST: /* N items, all different types */ { unsigned char count; int i; if (eventDataLen < 1) return -1; count = *eventData++; eventDataLen--; if (outBufLen > 0) { *outBuf++ = '['; outBufLen--; } else { goto no_room; } for (i = 0; i < count; i++) { result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen); if (result != 0) goto bail; if (i < count-1) { if (outBufLen > 0) { *outBuf++ = ','; outBufLen--; } else { goto no_room; } } } if (outBufLen > 0) { *outBuf++ = ']'; outBufLen--; } else { goto no_room; } } break; default: fprintf(stderr, "Unknown binary event type %d\n", type); return -1; } bail: *pEventData = eventData; *pEventDataLen = eventDataLen; *pOutBuf = outBuf; *pOutBufLen = outBufLen; return result; no_room: result = 1; goto bail; }
status_t LogSection::BlockingCall(int pipeWriteFd) const { // Open log buffer and getting logs since last retrieved time if any. unique_ptr<logger_list, void (*)(logger_list*)> loggers( gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() ? android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) : android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, gLastLogsRetrieved[mLogID], 0), android_logger_list_free); if (android_logger_open(loggers.get(), mLogID) == NULL) { ALOGE("LogSection %s: Can't get logger.", this->name.string()); return -1; } log_msg msg; log_time lastTimestamp(0); ProtoOutputStream proto; while (true) { // keeps reading until logd buffer is fully read. status_t err = android_logger_list_read(loggers.get(), &msg); // err = 0 - no content, unexpected connection drop or EOF. // err = +ive number - size of retrieved data from logger // err = -ive number, OS supplied error _except_ for -EAGAIN // err = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end of data. if (err <= 0) { if (err != -EAGAIN) { ALOGW("LogSection %s: fails to read a log_msg.\n", this->name.string()); } // dump previous logs and don't consider this error a failure. break; } if (mBinary) { // remove the first uint32 which is tag's index in event log tags android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t)); ; android_log_list_element elem; lastTimestamp.tv_sec = msg.entry_v1.sec; lastTimestamp.tv_nsec = msg.entry_v1.nsec; // format a BinaryLogEntry uint64_t token = proto.start(LogProto::BINARY_LOGS); proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec); proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec); proto.write(BinaryLogEntry::UID, (int)msg.entry_v4.uid); proto.write(BinaryLogEntry::PID, msg.entry_v1.pid); proto.write(BinaryLogEntry::TID, msg.entry_v1.tid); proto.write(BinaryLogEntry::TAG_INDEX, get4LE(reinterpret_cast<uint8_t const*>(msg.msg()))); do { elem = android_log_read_next(context); uint64_t elemToken = proto.start(BinaryLogEntry::ELEMS); switch (elem.type) { case EVENT_TYPE_INT: proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_INT); proto.write(BinaryLogEntry::Elem::VAL_INT32, (int)elem.data.int32); break; case EVENT_TYPE_LONG: proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LONG); proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long)elem.data.int64); break; case EVENT_TYPE_STRING: proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_STRING); proto.write(BinaryLogEntry::Elem::VAL_STRING, elem.data.string, elem.len); break; case EVENT_TYPE_FLOAT: proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_FLOAT); proto.write(BinaryLogEntry::Elem::VAL_FLOAT, elem.data.float32); break; case EVENT_TYPE_LIST: proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST); break; case EVENT_TYPE_LIST_STOP: proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP); break; case EVENT_TYPE_UNKNOWN: proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN); break; } proto.end(elemToken); } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete); proto.end(token); if (context) { android_log_destroy(&context); } } else { AndroidLogEntry entry; err = android_log_processLogBuffer(&msg.entry_v1, &entry); if (err != NO_ERROR) { ALOGW("LogSection %s: fails to process to an entry.\n", this->name.string()); break; } lastTimestamp.tv_sec = entry.tv_sec; lastTimestamp.tv_nsec = entry.tv_nsec; // format a TextLogEntry uint64_t token = proto.start(LogProto::TEXT_LOGS); proto.write(TextLogEntry::SEC, (long long)entry.tv_sec); proto.write(TextLogEntry::NANOSEC, (long long)entry.tv_nsec); proto.write(TextLogEntry::PRIORITY, (int)entry.priority); proto.write(TextLogEntry::UID, entry.uid); proto.write(TextLogEntry::PID, entry.pid); proto.write(TextLogEntry::TID, entry.tid); proto.write(TextLogEntry::TAG, entry.tag, trimTail(entry.tag, entry.tagLen)); proto.write(TextLogEntry::LOG, entry.message, trimTail(entry.message, entry.messageLen)); proto.end(token); } } gLastLogsRetrieved[mLogID] = lastTimestamp; if (!proto.flush(pipeWriteFd) && errno == EPIPE) { ALOGE("[%s] wrote to a broken pipe\n", this->name.string()); return EPIPE; } return NO_ERROR; }
/** * Convert a binary log entry to ASCII form. * * For convenience we mimic the processLogBuffer API. There is no * pre-defined output length for the binary data, since we're free to format * it however we choose, which means we can't really use a fixed-size buffer * here. */ LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer( struct logger_entry *buf, AndroidLogEntry *entry, const EventTagMap *map, char *messageBuf, int messageBufLen) { size_t inCount; unsigned int tagIndex; const unsigned char* eventData; entry->tv_sec = buf->sec; entry->tv_nsec = buf->nsec; entry->priority = ANDROID_LOG_INFO; entry->uid = -1; entry->pid = buf->pid; entry->tid = buf->tid; /* * Pull the tag out, fill in some additional details based on incoming * buffer version (v3 adds lid, v4 adds uid). */ eventData = (const unsigned char*) buf->msg; struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf; if (buf2->hdr_size) { if ((buf2->hdr_size < sizeof(((struct log_msg *)NULL)->entry_v1)) || (buf2->hdr_size > sizeof(((struct log_msg *)NULL)->entry))) { fprintf(stderr, "+++ LOG: entry illegal hdr_size\n"); return -1; } eventData = ((unsigned char *)buf2) + buf2->hdr_size; if ((buf2->hdr_size >= sizeof(struct logger_entry_v3)) && (((struct logger_entry_v3 *)buf)->lid == LOG_ID_SECURITY)) { entry->priority = ANDROID_LOG_WARN; } if (buf2->hdr_size >= sizeof(struct logger_entry_v4)) { entry->uid = ((struct logger_entry_v4 *)buf)->uid; } } inCount = buf->len; if (inCount < 4) return -1; tagIndex = get4LE(eventData); eventData += 4; inCount -= 4; entry->tagLen = 0; if (map != NULL) { entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex); } else { entry->tag = NULL; } /* * If we don't have a map, or didn't find the tag number in the map, * stuff a generated tag value into the start of the output buffer and * shift the buffer pointers down. */ if (entry->tag == NULL) { size_t tagLen; tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); if (tagLen >= (size_t)messageBufLen) { tagLen = messageBufLen - 1; } entry->tag = messageBuf; entry->tagLen = tagLen; messageBuf += tagLen + 1; messageBufLen -= tagLen + 1; } /* * Format the event log data into the buffer. */ char* outBuf = messageBuf; size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ int result; result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining); if (result < 0) { fprintf(stderr, "Binary log entry conversion failed\n"); return -1; } else if (result == 1) { if (outBuf > messageBuf) { /* leave an indicator */ *(outBuf-1) = '!'; } else { /* no room to output anything at all */ *outBuf++ = '!'; outRemaining--; } /* pretend we ate all the data */ inCount = 0; } /* eat the silly terminating '\n' */ if (inCount == 1 && *eventData == '\n') { eventData++; inCount--; } if (inCount != 0) { fprintf(stderr, "Warning: leftover binary log data (%zu bytes)\n", inCount); } /* * Terminate the buffer. The NUL byte does not count as part of * entry->messageLen. */ *outBuf = '\0'; entry->messageLen = outBuf - messageBuf; assert(entry->messageLen == (messageBufLen-1) - outRemaining); entry->message = messageBuf; return 0; }
bool ObbFile::parseObbFile(int fd) { off64_t fileLength = lseek64(fd, 0, SEEK_END); if (fileLength < kFooterMinSize) { if (fileLength < 0) { ALOGW("error seeking in ObbFile: %s\n", strerror(errno)); } else { ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); } return false; } ssize_t actual; size_t footerSize; { lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); char *footer = new char[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); if (actual != kFooterTagSize) { ALOGW("couldn't read footer signature: %s\n", strerror(errno)); return false; } unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); if (fileSig != kSignature) { ALOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", kSignature, fileSig); return false; } footerSize = get4LE((unsigned char*)footer); if (footerSize > (size_t)fileLength - kFooterTagSize || footerSize > kMaxBufSize) { ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", footerSize, fileLength); return false; } if (footerSize < (kFooterMinSize - kFooterTagSize)) { ALOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", footerSize, kFooterMinSize - kFooterTagSize); return false; } } off64_t fileOffset = fileLength - footerSize - kFooterTagSize; if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); return false; } mFooterStart = fileOffset; char* scanBuf = (char*)malloc(footerSize); if (scanBuf == NULL) { ALOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); return false; } actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); // readAmount is guaranteed to be less than kMaxBufSize if (actual != (ssize_t)footerSize) { ALOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); free(scanBuf); return false; } #ifdef DEBUG for (int i = 0; i < footerSize; ++i) { ALOGI("char: 0x%02x\n", scanBuf[i]); } #endif uint32_t sigVersion = get4LE((unsigned char*)scanBuf); if (sigVersion != kSigVersion) { ALOGW("Unsupported ObbFile version %d\n", sigVersion); free(scanBuf); return false; } mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset); memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); if (packageNameLen == 0 || packageNameLen > (footerSize - kPackageNameOffset)) { ALOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; } char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset); mPackageName = String8(const_cast<char*>(packageName), packageNameLen); free(scanBuf); #ifdef DEBUG ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); #endif return true; }
/* * Get the useful fields from the zip entry. * * Returns non-zero if the contents of the fields (particularly the data * offset) appear to be bogus. */ int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry, int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { int ent = entryToIndex(pArchive, entry); if (ent < 0) return -1; /* * Recover the start of the central directory entry from the filename * pointer. The filename is the first entry past the fixed-size data, * so we can just subtract back from that. */ const unsigned char* basePtr = (const unsigned char*) pArchive->mDirectoryMap.addr; const unsigned char* ptr = (const unsigned char*) pArchive->mHashTable[ent].name; off_t cdOffset = pArchive->mDirectoryOffset; ptr -= kCDELen; int method = get2LE(ptr + kCDEMethod); if (pMethod != NULL) *pMethod = method; if (pModWhen != NULL) *pModWhen = get4LE(ptr + kCDEModWhen); if (pCrc32 != NULL) *pCrc32 = get4LE(ptr + kCDECRC); size_t compLen = get4LE(ptr + kCDECompLen); if (pCompLen != NULL) *pCompLen = compLen; size_t uncompLen = get4LE(ptr + kCDEUncompLen); if (pUncompLen != NULL) *pUncompLen = uncompLen; /* * If requested, determine the offset of the start of the data. All we * have is the offset to the Local File Header, which is variable size, * so we have to read the contents of the struct to figure out where * the actual data starts. * * We also need to make sure that the lengths are not so large that * somebody trying to map the compressed or uncompressed data runs * off the end of the mapped region. * * Note we don't verify compLen/uncompLen if they don't request the * dataOffset, because dataOffset is expensive to determine. However, * if they don't have the file offset, they're not likely to be doing * anything with the contents. */ if (pOffset != NULL) { long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); if (localHdrOffset + kLFHLen >= cdOffset) { LOGW("Zip: bad local hdr offset in zip\n"); return -1; } u1 lfhBuf[kLFHLen]; if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { LOGW("Zip: failed seeking to lfh at offset %ld\n", localHdrOffset); return -1; } ssize_t actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf))); if (actual != sizeof(lfhBuf)) { LOGW("Zip: failed reading lfh from offset %ld\n", localHdrOffset); return -1; } if (get4LE(lfhBuf) != kLFHSignature) { LOGW("Zip: didn't find signature at start of lfh, offset=%ld\n", localHdrOffset); return -1; } off_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); if (dataOffset >= cdOffset) { LOGW("Zip: bad data offset %ld in zip\n", (long) dataOffset); return -1; } /* check lengths */ if ((off_t)(dataOffset + compLen) > cdOffset) { LOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)\n", (long) dataOffset, compLen, (long) cdOffset); return -1; } if (method == kCompressStored && (off_t)(dataOffset + uncompLen) > cdOffset) { LOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)\n", (long) dataOffset, uncompLen, (long) cdOffset); return -1; } *pOffset = dataOffset; } return 0; }
/* * Find the zip Central Directory and memory-map it. * * On success, returns 0 after populating fields from the EOCD area: * mDirectoryOffset * mDirectoryMap * mNumEntries */ static int mapCentralDirectory(int fd, const char* debugFileName, ZipArchive* pArchive) { u1* scanBuf = NULL; int result = -1; /* * Get and test file length. */ off_t fileLength = lseek(fd, 0, SEEK_END); if (fileLength < kEOCDLen) { LOGV("Zip: length %ld is too small to be zip\n", (long) fileLength); goto bail; } /* * Perform the traditional EOCD snipe hunt. * * We're searching for the End of Central Directory magic number, * which appears at the start of the EOCD block. It's followed by * 18 bytes of EOCD stuff and up to 64KB of archive comment. We * need to read the last part of the file into a buffer, dig through * it to find the magic number, parse some values out, and use those * to determine the extent of the CD. * * We start by pulling in the last part of the file. */ size_t readAmount = kMaxEOCDSearch; if (readAmount > (size_t) fileLength) readAmount = fileLength; off_t searchStart = fileLength - readAmount; scanBuf = (u1*) malloc(readAmount); if (lseek(fd, searchStart, SEEK_SET) != searchStart) { LOGW("Zip: seek %ld failed: %s\n", (long) searchStart, strerror(errno)); goto bail; } ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); goto bail; } /* * Scan backward for the EOCD magic. In an archive without a trailing * comment, we'll find it on the first try. (We may want to consider * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ int i; for (i = readAmount - kEOCDLen; i >= 0; i--) { if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { LOGV("+++ Found EOCD at buf+%d\n", i); break; } } if (i < 0) { LOGD("Zip: EOCD not found, %s is not zip\n", debugFileName); goto bail; } off_t eocdOffset = searchStart + i; const u1* eocdPtr = scanBuf + i; assert(eocdOffset < fileLength); /* * Grab the CD offset and size, and the number of entries in the * archive. Verify that they look reasonable. */ u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); u4 dirSize = get4LE(eocdPtr + kEOCDSize); u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset); if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)\n", (long) dirOffset, dirSize, (long) eocdOffset); goto bail; } if (numEntries == 0) { LOGW("Zip: empty archive?\n"); goto bail; } LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", numEntries, dirSize, dirOffset); /* * It all looks good. Create a mapping for the CD, and set the fields * in pArchive. */ if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize, &pArchive->mDirectoryMap) != 0) { LOGW("Zip: cd map failed\n"); goto bail; } pArchive->mNumEntries = numEntries; pArchive->mDirectoryOffset = dirOffset; result = 0; bail: free(scanBuf); return result; }
/* * Parses the Zip archive's Central Directory. Allocates and populates the * hash table. * * Returns 0 on success. */ static int parseZipArchive(ZipArchive* pArchive) { int result = -1; const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr; size_t cdLength = pArchive->mDirectoryMap.length; int numEntries = pArchive->mNumEntries; /* * Create hash table. We have a minimum 75% load factor, possibly as * low as 50% after we round off to a power of 2. There must be at * least one unused entry to avoid an infinite loop during creation. */ pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3); pArchive->mHashTable = (ZipHashEntry*) calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry)); /* * Walk through the central directory, adding entries to the hash * table and verifying values. */ const u1* ptr = cdPtr; int i; for (i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { LOGW("Zip: missed a central dir sig (at %d)\n", i); goto bail; } if (ptr + kCDELen > cdPtr + cdLength) { LOGW("Zip: ran off the end (at %d)\n", i); goto bail; } long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); if (localHdrOffset >= pArchive->mDirectoryOffset) { LOGW("Zip: bad LFH offset %ld at entry %d\n", localHdrOffset, i); goto bail; } unsigned int fileNameLen, extraLen, commentLen, hash; fileNameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); /* add the CDE filename to the hash table */ hash = computeHash((const char*)ptr + kCDELen, fileNameLen); addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash); ptr += kCDELen + fileNameLen + extraLen + commentLen; if ((size_t)(ptr - cdPtr) > cdLength) { LOGW("Zip: bad CD advance (%d vs %zd) at entry %d\n", (int) (ptr - cdPtr), cdLength, i); goto bail; } } LOGV("+++ zip good scan %d entries\n", numEntries); result = 0; bail: return result; }
/* * Get the useful fields from the zip entry. * * Returns "false" if the offsets to the fields or the contents of the fields * appear to be bogus. */ bool dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry, int* pMethod, long* pUncompLen, long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { int ent = entryToIndex(pArchive, entry); if (ent < 0) return false; /* * Recover the start of the central directory entry from the filename * pointer. */ const unsigned char* basePtr = (const unsigned char*) pArchive->mMap.addr; const unsigned char* ptr = (const unsigned char*) pArchive->mHashTable[ent].name; size_t zipLength = pArchive->mMap.length; ptr -= kCDELen; int method = get2LE(ptr + kCDEMethod); if (pMethod != NULL) *pMethod = method; if (pModWhen != NULL) *pModWhen = get4LE(ptr + kCDEModWhen); if (pCrc32 != NULL) *pCrc32 = get4LE(ptr + kCDECRC); /* * We need to make sure that the lengths are not so large that somebody * trying to map the compressed or uncompressed data runs off the end * of the mapped region. */ unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); if (localHdrOffset + kLFHLen >= zipLength) { LOGE("ERROR: bad local hdr offset in zip\n"); return false; } const unsigned char* localHdr = basePtr + localHdrOffset; off_t dataOffset = localHdrOffset + kLFHLen + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); if ((unsigned long) dataOffset >= zipLength) { LOGE("ERROR: bad data offset in zip\n"); return false; } if (pCompLen != NULL) { *pCompLen = get4LE(ptr + kCDECompLen); if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { LOGE("ERROR: bad compressed length in zip\n"); return false; } } if (pUncompLen != NULL) { *pUncompLen = get4LE(ptr + kCDEUncompLen); if (*pUncompLen < 0) { LOGE("ERROR: negative uncompressed length in zip\n"); return false; } if (method == kCompressStored && (size_t)(dataOffset + *pUncompLen) >= zipLength) { LOGE("ERROR: bad uncompressed length in zip\n"); return false; } } if (pOffset != NULL) { *pOffset = dataOffset; } return true; }
void __android_log_btwrite_multiple__helper(int count) { log_time ts(CLOCK_MONOTONIC); struct logger_list *logger_list; ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, 0))); log_time ts1(CLOCK_MONOTONIC); pid_t pid = fork(); if (pid == 0) { // child for (int i = count; i; --i) { ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); usleep(100); } ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1))); usleep(1000000); _exit(0); } siginfo_t info{}; ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))); int expected_count = (count < 2) ? count : 2; int expected_chatty_count = (count <= 2) ? 0 : 1; int expected_expire_count = (count < 2) ? 0 : (count - 2); count = 0; int second_count = 0; int chatty_count = 0; int expire_count = 0; for (;;) { log_msg log_msg; if (android_logger_list_read(logger_list, &log_msg) <= 0) break; if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) || (log_msg.id() != LOG_ID_EVENTS)) continue; char *eventData = log_msg.msg(); if (!eventData) continue; uint32_t tag = get4LE(eventData); if ((eventData[4] == EVENT_TYPE_LONG) && (log_msg.entry.len == (4 + 1 + 8))) { if (tag != 0) continue; log_time tx(eventData + 4 + 1); if (ts == tx) { ++count; } else if (ts1 == tx) { ++second_count; } } else if (eventData[4] == EVENT_TYPE_STRING) { // chatty if (tag != 1004) continue; ++chatty_count; // int len = get4LE(eventData + 4 + 1); const char *cp = strstr(eventData + 4 + 1 + 4, " expire "); if (!cp) continue; unsigned val = 0; sscanf(cp, " expire %u lines", &val); expire_count += val; } } EXPECT_EQ(expected_count, count); EXPECT_EQ(1, second_count); EXPECT_EQ(expected_chatty_count, chatty_count); EXPECT_EQ(expected_expire_count, expire_count); android_logger_list_close(logger_list); }
/* * Parse the Zip archive, verifying its contents and initializing internal * data structures. */ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) { #define CHECK_OFFSET(_off) { \ if ((unsigned int) (_off) >= maxOffset) { \ LOGE("ERROR: bad offset %u (max %d): %s\n", \ (unsigned int) (_off), maxOffset, #_off); \ goto bail; \ } \ } bool result = false; const unsigned char* basePtr = (const unsigned char*)pMap->addr; const unsigned char* ptr; size_t length = pMap->length; unsigned int i, numEntries, cdOffset; unsigned int val; /* * The first 4 bytes of the file will either be the local header * signature for the first file (kLFHSignature) or, if the archive doesn't * have any files in it, the end-of-central-directory signature * (kEOCDSignature). */ val = get4LE(basePtr); if (val == kEOCDSignature) { LOGI("Found Zip archive, but it looks empty\n"); goto bail; } else if (val != kLFHSignature) { LOGV("Not a Zip archive (found 0x%08x)\n", val); goto bail; } /* * Find the EOCD. We'll find it immediately unless they have a file * comment. */ ptr = basePtr + length - kEOCDLen; while (ptr >= basePtr) { if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) break; ptr--; } if (ptr < basePtr) { LOGI("Could not find end-of-central-directory in Zip\n"); goto bail; } /* * There are two interesting items in the EOCD block: the number of * entries in the file, and the file offset of the start of the * central directory. * * (There's actually a count of the #of entries in this file, and for * all files which comprise a spanned archive, but for our purposes * we're only interested in the current file. Besides, we expect the * two to be equivalent for our stuff.) */ numEntries = get2LE(ptr + kEOCDNumEntries); cdOffset = get4LE(ptr + kEOCDFileOffset); /* valid offsets are [0,EOCD] */ unsigned int maxOffset; maxOffset = (ptr - basePtr) +1; LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); if (numEntries == 0 || cdOffset >= length) { LOGW("Invalid entries=%d offset=%d (len=%zd)\n", numEntries, cdOffset, length); goto bail; } /* * Create hash table. We have a minimum 75% load factor, possibly as * low as 50% after we round off to a power of 2. There must be at * least one unused entry to avoid an infinite loop during creation. */ pArchive->mNumEntries = numEntries; pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3); pArchive->mHashTable = (ZipHashEntry*) calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry)); /* * Walk through the central directory, adding entries to the hash * table. */ ptr = basePtr + cdOffset; for (i = 0; i < numEntries; i++) { unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; const unsigned char* localHdr; unsigned int hash; if (get4LE(ptr) != kCDESignature) { LOGW("Missed a central dir sig (at %d)\n", i); goto bail; } if (ptr + kCDELen > basePtr + length) { LOGW("Ran off the end (at %d)\n", i); goto bail; } localHdrOffset = get4LE(ptr + kCDELocalOffset); CHECK_OFFSET(localHdrOffset); fileNameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", // i, localHdrOffset, fileNameLen, extraLen, commentLen); //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); /* add the CDE filename to the hash table */ hash = computeHash((const char*)ptr + kCDELen, fileNameLen); addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash); localHdr = basePtr + localHdrOffset; if (get4LE(localHdr) != kLFHSignature) { LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i); goto bail; } ptr += kCDELen + fileNameLen + extraLen + commentLen; CHECK_OFFSET(ptr - basePtr); } result = true; bail: return result; #undef CHECK_OFFSET }
static int mapCentralDirectory0(int fd, const char* debugFileName, ZipArchive* pArchive, off_t fileLength, size_t readAmount, u1* scanBuf) { off_t searchStart = fileLength - readAmount; if (lseek(fd, searchStart, SEEK_SET) != searchStart) { LOGW("Zip: seek %ld failed: %s", (long) searchStart, strerror(errno)); return -1; } ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { LOGW("Zip: read %zd failed: %s", readAmount, strerror(errno)); return -1; } /* * Scan backward for the EOCD magic. In an archive without a trailing * comment, we'll find it on the first try. (We may want to consider * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ int i; for (i = readAmount - kEOCDLen; i >= 0; i--) { if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { LOGV("+++ Found EOCD at buf+%d", i); break; } } if (i < 0) { LOGD("Zip: EOCD not found, %s is not zip", debugFileName); return -1; } off_t eocdOffset = searchStart + i; const u1* eocdPtr = scanBuf + i; assert(eocdOffset < fileLength); /* * Grab the CD offset and size, and the number of entries in the * archive. Verify that they look reasonable. */ u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); u4 dirSize = get4LE(eocdPtr + kEOCDSize); u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset); if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)", (long) dirOffset, dirSize, (long) eocdOffset); return -1; } if (numEntries == 0) { LOGW("Zip: empty archive?"); return -1; } LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d", numEntries, dirSize, dirOffset); /* * It all looks good. Create a mapping for the CD, and set the fields * in pArchive. */ if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize, &pArchive->mDirectoryMap) != 0) { LOGW("Zip: cd map failed"); return -1; } pArchive->mNumEntries = numEntries; pArchive->mDirectoryOffset = dirOffset; return 0; }
int main(int argc, char** argv) { // get file size if( argc != 2) { printf("usage: zip [zipfilename]\n"); return -1; } struct stat zipstat; int ret = stat(argv[1], &zipstat); if (ret != 0) { printf("stat file(%s) failed, %s\n", argv[1], strerror(errno)); return -1; } long zipFileLength = zipstat.st_size; // get EOCD int fd = open(argv[1], O_RDONLY); if (fd == -1) { printf("can not open %s\n", argv[1]); return -1; } unsigned char eocdBuffer[ENDHDR]; lseek(fd, zipFileLength - ENDHDR, SEEK_SET); read(fd, eocdBuffer, ENDHDR); unsigned int entrynum = get2LE(eocdBuffer + ENDSUB); unsigned int entryoffset = get4LE(eocdBuffer + ENDOFF); // search central directory area lseek(fd, entryoffset, SEEK_SET); int i = 0; unsigned char cdBuffer[CENHDR]; uint32_t fileNameLen = 0; uint32_t extraLen = 0; uint32_t fileCommentLen = 0; uint32_t u32offset = entryoffset; char nameBuffer[PATH_MAX]; for(i = 0; i < entrynum; i++) { printf("CD info offset: 0x%x\n", u32offset); read(fd, cdBuffer, CENHDR); uint32_t u32sig = get4LE(cdBuffer); if (u32sig != 0x02014b50) { printf("central directory sig error: 0x%x, expert 0x02014b50\n", u32sig); close(fd); return -1; } fileNameLen = get2LE(cdBuffer + CENNAM); extraLen = get2LE(cdBuffer + CENEXT); fileCommentLen = get2LE(cdBuffer + CENCOM); printf("fileNameLen: %d, extraLen: %d, fileCommentLen: %d\n", fileNameLen, extraLen, fileCommentLen); if (fileNameLen > PATH_MAX) { printf("file name length(0x%x) should not larger than PATH_MAX\n", fileNameLen); close(fd); return -1; } read(fd, nameBuffer, fileNameLen); nameBuffer[fileNameLen] = '\0'; printf("num %d file name: %s\n", i, nameBuffer); // seek next central directory u32offset += CENHDR + fileNameLen + extraLen + fileCommentLen; lseek(fd, u32offset, SEEK_SET); } close(fd); return 0; }
/* * Parses the Zip archive's Central Directory. Allocates and populates the * hash table. * * Returns 0 on success. */ static int parseZipArchive(ZipArchive* pArchive) { int result = -1; const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr; size_t cdLength = pArchive->mDirectoryMap.length; int numEntries = pArchive->mNumEntries; /* * Create hash table. We have a minimum 75% load factor, possibly as * low as 50% after we round off to a power of 2. There must be at * least one unused entry to avoid an infinite loop during creation. */ pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3); pArchive->mHashTable = (ZipHashEntry*) calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry)); /* * Walk through the central directory, adding entries to the hash * table and verifying values. */ const u1* ptr = cdPtr; int i; for (i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { ALOGW("Zip: missed a central dir sig (at %d)", i); goto bail; } if (ptr + kCDELen > cdPtr + cdLength) { ALOGW("Zip: ran off the end (at %d)", i); goto bail; } long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); if (localHdrOffset >= pArchive->mDirectoryOffset) { ALOGW("Zip: bad LFH offset %ld at entry %d", localHdrOffset, i); goto bail; } unsigned int gpbf = get2LE(ptr + kCDEGPBFlags); if ((gpbf & kGPFUnsupportedMask) != 0) { ALOGW("Invalid General Purpose Bit Flag: %d", gpbf); goto bail; } unsigned int nameLen, extraLen, commentLen, hash; nameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); const char *name = (const char *) ptr + kCDELen; /* Check name for NULL characters */ if (memchr(name, 0, nameLen) != NULL) { ALOGW("Filename contains NUL byte"); goto bail; } /* add the CDE filename to the hash table */ hash = computeHash(name, nameLen); addToHash(pArchive, name, nameLen, hash); /* We don't care about the comment or extra data. */ ptr += kCDELen + nameLen + extraLen + commentLen; if ((size_t)(ptr - cdPtr) > cdLength) { ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d", (int) (ptr - cdPtr), cdLength, i); goto bail; } } ALOGV("+++ zip good scan %d entries", numEntries); result = 0; bail: return result; }
static int mapCentralDirectory0(int fd, const char* debugFileName, ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf) { /* * Make sure this is a Zip archive. */ if (lseek64(pArchive->mFd, 0, SEEK_SET) != 0) { ALOGW("seek to start failed: %s", strerror(errno)); return false; } ssize_t actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, sizeof(int32_t))); if (actual != (ssize_t) sizeof(int32_t)) { ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); return false; } unsigned int header = get4LE(scanBuf); if (header != kLFHSignature) { ALOGV("Not a Zip archive (found 0x%08x)\n", header); return false; } /* * Perform the traditional EOCD snipe hunt. * * We're searching for the End of Central Directory magic number, * which appears at the start of the EOCD block. It's followed by * 18 bytes of EOCD stuff and up to 64KB of archive comment. We * need to read the last part of the file into a buffer, dig through * it to find the magic number, parse some values out, and use those * to determine the extent of the CD. * * We start by pulling in the last part of the file. */ off64_t searchStart = fileLength - readAmount; if (lseek64(pArchive->mFd, searchStart, SEEK_SET) != searchStart) { ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); return false; } actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { ALOGW("Zip: read %zd, expected %zd. Failed: %s\n", actual, readAmount, strerror(errno)); return false; } /* * Scan backward for the EOCD magic. In an archive without a trailing * comment, we'll find it on the first try. (We may want to consider * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ int i; for (i = readAmount - kEOCDLen; i >= 0; i--) { if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { ALOGV("+++ Found EOCD at buf+%d", i); break; } } if (i < 0) { ALOGD("Zip: EOCD not found, %s is not zip", debugFileName); return -1; } off64_t eocdOffset = searchStart + i; const u1* eocdPtr = scanBuf + i; assert(eocdOffset < fileLength); /* * Grab the CD offset and size, and the number of entries in the * archive. Verify that they look reasonable. */ u4 diskNumber = get2LE(eocdPtr + kEOCDDiskNumber); u4 diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD); u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); u4 totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries); u4 centralDirSize = get4LE(eocdPtr + kEOCDSize); u4 centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset); u4 commentSize = get2LE(eocdPtr + kEOCDCommentSize); // Verify that they look reasonable. if ((long long) centralDirOffset + (long long) centralDirSize > (long long) eocdOffset) { ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", (long) centralDirOffset, centralDirSize, (long) eocdOffset); return false; } if (numEntries == 0) { ALOGW("empty archive?\n"); return false; } else if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) { ALOGW("spanned archives not supported"); return false; } // Check to see if comment is a sane size if (((size_t) commentSize > (fileLength - kEOCDLen)) || (eocdOffset > (fileLength - kEOCDLen) - commentSize)) { ALOGW("comment size runs off end of file"); return false; } ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", numEntries, centralDirSize, centralDirOffset); /* * It all looks good. Create a mapping for the CD, and set the fields * in pArchive. */ if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize, &pArchive->mDirectoryMap) != 0) { ALOGW("Zip: cd map failed"); return -1; } pArchive->mNumEntries = numEntries; pArchive->mDirectoryOffset = centralDirOffset; return 0; }