|  | /* | 
|  | * Copyright (C) 2006 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | // | 
|  | // General-purpose Zip archive access.  This class allows both reading and | 
|  | // writing to Zip archives, including deletion of existing entries. | 
|  | // | 
|  | #ifndef __LIBS_ZIPFILE_H | 
|  | #define __LIBS_ZIPFILE_H | 
|  |  | 
|  | #include <utils/Vector.h> | 
|  | #include <utils/Errors.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include "ZipEntry.h" | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | /* | 
|  | * Manipulate a Zip archive. | 
|  | * | 
|  | * Some changes will not be visible in the until until "flush" is called. | 
|  | * | 
|  | * The correct way to update a file archive is to make all changes to a | 
|  | * copy of the archive in a temporary file, and then unlink/rename over | 
|  | * the original after everything completes.  Because we're only interested | 
|  | * in using this for packaging, we don't worry about such things.  Crashing | 
|  | * after making changes and before flush() completes could leave us with | 
|  | * an unusable Zip archive. | 
|  | */ | 
|  | class ZipFile { | 
|  | public: | 
|  | ZipFile(void) | 
|  | : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) | 
|  | {} | 
|  | ~ZipFile(void) { | 
|  | if (!mReadOnly) | 
|  | flush(); | 
|  | if (mZipFp != NULL) | 
|  | fclose(mZipFp); | 
|  | discardEntries(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Open a new or existing archive. | 
|  | */ | 
|  | enum { | 
|  | kOpenReadOnly   = 0x01, | 
|  | kOpenReadWrite  = 0x02, | 
|  | kOpenCreate     = 0x04,     // create if it doesn't exist | 
|  | kOpenTruncate   = 0x08,     // if it exists, empty it | 
|  | }; | 
|  | status_t open(const char* zipFileName, int flags); | 
|  |  | 
|  | /* | 
|  | * Add a file to the end of the archive.  Specify whether you want the | 
|  | * library to try to store it compressed. | 
|  | * | 
|  | * If "storageName" is specified, the archive will use that instead | 
|  | * of "fileName". | 
|  | * | 
|  | * If there is already an entry with the same name, the call fails. | 
|  | * Existing entries with the same name must be removed first. | 
|  | * | 
|  | * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. | 
|  | */ | 
|  | status_t add(const char* fileName, int compressionMethod, | 
|  | ZipEntry** ppEntry) | 
|  | { | 
|  | return add(fileName, fileName, compressionMethod, ppEntry); | 
|  | } | 
|  | status_t add(const char* fileName, const char* storageName, | 
|  | int compressionMethod, ZipEntry** ppEntry) | 
|  | { | 
|  | return addCommon(fileName, NULL, 0, storageName, | 
|  | ZipEntry::kCompressStored, | 
|  | compressionMethod, ppEntry); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add a file that is already compressed with gzip. | 
|  | * | 
|  | * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. | 
|  | */ | 
|  | status_t addGzip(const char* fileName, const char* storageName, | 
|  | ZipEntry** ppEntry) | 
|  | { | 
|  | return addCommon(fileName, NULL, 0, storageName, | 
|  | ZipEntry::kCompressDeflated, | 
|  | ZipEntry::kCompressDeflated, ppEntry); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add a file from an in-memory data buffer. | 
|  | * | 
|  | * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. | 
|  | */ | 
|  | status_t add(const void* data, size_t size, const char* storageName, | 
|  | int compressionMethod, ZipEntry** ppEntry) | 
|  | { | 
|  | return addCommon(NULL, data, size, storageName, | 
|  | ZipEntry::kCompressStored, | 
|  | compressionMethod, ppEntry); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add an entry by copying it from another zip file.  If "padding" is | 
|  | * nonzero, the specified number of bytes will be added to the "extra" | 
|  | * field in the header. | 
|  | * | 
|  | * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. | 
|  | */ | 
|  | status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, | 
|  | int padding, ZipEntry** ppEntry); | 
|  |  | 
|  | /* | 
|  | * Add an entry by copying it from another zip file, recompressing with | 
|  | * Zopfli if already compressed. | 
|  | * | 
|  | * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. | 
|  | */ | 
|  | status_t addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, | 
|  | ZipEntry** ppEntry); | 
|  |  | 
|  | /* | 
|  | * Mark an entry as having been removed.  It is not actually deleted | 
|  | * from the archive or our internal data structures until flush() is | 
|  | * called. | 
|  | */ | 
|  | status_t remove(ZipEntry* pEntry); | 
|  |  | 
|  | /* | 
|  | * Flush changes.  If mNeedCDRewrite is set, this writes the central dir. | 
|  | */ | 
|  | status_t flush(void); | 
|  |  | 
|  | /* | 
|  | * Expand the data into the buffer provided.  The buffer must hold | 
|  | * at least <uncompressed len> bytes.  Variation expands directly | 
|  | * to a file. | 
|  | * | 
|  | * Returns "false" if an error was encountered in the compressed data. | 
|  | */ | 
|  | //bool uncompress(const ZipEntry* pEntry, void* buf) const; | 
|  | //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; | 
|  | void* uncompress(const ZipEntry* pEntry) const; | 
|  |  | 
|  | /* | 
|  | * Get an entry, by name.  Returns NULL if not found. | 
|  | * | 
|  | * Does not return entries pending deletion. | 
|  | */ | 
|  | ZipEntry* getEntryByName(const char* fileName) const; | 
|  |  | 
|  | /* | 
|  | * Get the Nth entry in the archive. | 
|  | * | 
|  | * This will return an entry that is pending deletion. | 
|  | */ | 
|  | int getNumEntries(void) const { return mEntries.size(); } | 
|  | ZipEntry* getEntryByIndex(int idx) const; | 
|  |  | 
|  | private: | 
|  | /* these are private and not defined */ | 
|  | ZipFile(const ZipFile& src); | 
|  | ZipFile& operator=(const ZipFile& src); | 
|  |  | 
|  | class EndOfCentralDir { | 
|  | public: | 
|  | EndOfCentralDir(void) : | 
|  | mDiskNumber(0), | 
|  | mDiskWithCentralDir(0), | 
|  | mNumEntries(0), | 
|  | mTotalNumEntries(0), | 
|  | mCentralDirSize(0), | 
|  | mCentralDirOffset(0), | 
|  | mCommentLen(0), | 
|  | mComment(NULL) | 
|  | {} | 
|  | virtual ~EndOfCentralDir(void) { | 
|  | delete[] mComment; | 
|  | } | 
|  |  | 
|  | status_t readBuf(const uint8_t* buf, int len); | 
|  | status_t write(FILE* fp); | 
|  |  | 
|  | //uint32_t mSignature; | 
|  | uint16_t mDiskNumber; | 
|  | uint16_t mDiskWithCentralDir; | 
|  | uint16_t mNumEntries; | 
|  | uint16_t mTotalNumEntries; | 
|  | uint32_t mCentralDirSize; | 
|  | uint32_t mCentralDirOffset;      // offset from first disk | 
|  | uint16_t mCommentLen; | 
|  | uint8_t* mComment; | 
|  |  | 
|  | enum { | 
|  | kSignature      = 0x06054b50, | 
|  | kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment | 
|  |  | 
|  | kMaxCommentLen  = 65535,    // longest possible in ushort | 
|  | kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen, | 
|  |  | 
|  | }; | 
|  |  | 
|  | void dump(void) const; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* read all entries in the central dir */ | 
|  | status_t readCentralDir(void); | 
|  |  | 
|  | /* crunch deleted entries out */ | 
|  | status_t crunchArchive(void); | 
|  |  | 
|  | /* clean up mEntries */ | 
|  | void discardEntries(void); | 
|  |  | 
|  | /* common handler for all "add" functions */ | 
|  | status_t addCommon(const char* fileName, const void* data, size_t size, | 
|  | const char* storageName, int sourceType, int compressionMethod, | 
|  | ZipEntry** ppEntry); | 
|  |  | 
|  | /* copy all of "srcFp" into "dstFp" */ | 
|  | status_t copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32); | 
|  | /* copy all of "data" into "dstFp" */ | 
|  | status_t copyDataToFp(FILE* dstFp, | 
|  | const void* data, size_t size, uint32_t* pCRC32); | 
|  | /* copy some of "srcFp" into "dstFp" */ | 
|  | status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, | 
|  | uint32_t* pCRC32); | 
|  | /* like memmove(), but on parts of a single file */ | 
|  | status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); | 
|  | /* compress all of "srcFp" into "dstFp", using Deflate */ | 
|  | status_t compressFpToFp(FILE* dstFp, FILE* srcFp, | 
|  | const void* data, size_t size, uint32_t* pCRC32); | 
|  |  | 
|  | /* get modification date from a file descriptor */ | 
|  | time_t getModTime(int fd); | 
|  |  | 
|  | /* | 
|  | * We use stdio FILE*, which gives us buffering but makes dealing | 
|  | * with files >2GB awkward.  Until we support Zip64, we're fine. | 
|  | */ | 
|  | FILE*           mZipFp;             // Zip file pointer | 
|  |  | 
|  | /* one of these per file */ | 
|  | EndOfCentralDir mEOCD; | 
|  |  | 
|  | /* did we open this read-only? */ | 
|  | bool            mReadOnly; | 
|  |  | 
|  | /* set this when we trash the central dir */ | 
|  | bool            mNeedCDRewrite; | 
|  |  | 
|  | /* | 
|  | * One ZipEntry per entry in the zip file.  I'm using pointers instead | 
|  | * of objects because it's easier than making operator= work for the | 
|  | * classes and sub-classes. | 
|  | */ | 
|  | Vector<ZipEntry*>   mEntries; | 
|  | }; | 
|  |  | 
|  | }; // namespace android | 
|  |  | 
|  | #endif // __LIBS_ZIPFILE_H |