| /* | |
| * 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); | |
| /* | |
| * 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); | |
| /* | |
| * 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 unsigned char* buf, int len); | |
| status_t write(FILE* fp); | |
| //unsigned long mSignature; | |
| unsigned short mDiskNumber; | |
| unsigned short mDiskWithCentralDir; | |
| unsigned short mNumEntries; | |
| unsigned short mTotalNumEntries; | |
| unsigned long mCentralDirSize; | |
| unsigned long mCentralDirOffset; // offset from first disk | |
| unsigned short mCommentLen; | |
| unsigned char* 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, unsigned long* pCRC32); | |
| /* copy all of "data" into "dstFp" */ | |
| status_t copyDataToFp(FILE* dstFp, | |
| const void* data, size_t size, unsigned long* pCRC32); | |
| /* copy some of "srcFp" into "dstFp" */ | |
| status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, | |
| unsigned long* 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, unsigned long* 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 |