/* * 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 #include #include 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 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 mEntries; }; }; // namespace aapt #endif // __LIBS_ZIPFILE_H