| /* |
| * 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 "BigBuffer.h" |
| #include "ZipEntry.h" |
| |
| #include <stdio.h> |
| #include <utils/Errors.h> |
| #include <vector> |
| |
| namespace aapt { |
| |
| using android::status_t; |
| |
| /* |
| * 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); |
| } |
| |
| status_t add(const BigBuffer& data, const char* storageName, |
| int compressionMethod, ZipEntry** ppEntry); |
| |
| /* |
| * Add an entry by copying it from another zip file. If storageName is |
| * non-NULL, the entry will be inserted with the name storageName, otherwise |
| * it will have the same name as the source entry. 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, |
| const char* storageName, 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. |
| */ |
| std::vector<ZipEntry*> mEntries; |
| }; |
| |
| }; // namespace aapt |
| |
| #endif // __LIBS_ZIPFILE_H |