1*d57664e9SAndroid Build Coastguard Worker /* 2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2006 The Android Open Source Project 3*d57664e9SAndroid Build Coastguard Worker * 4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*d57664e9SAndroid Build Coastguard Worker * 8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*d57664e9SAndroid Build Coastguard Worker * 10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*d57664e9SAndroid Build Coastguard Worker * limitations under the License. 15*d57664e9SAndroid Build Coastguard Worker */ 16*d57664e9SAndroid Build Coastguard Worker 17*d57664e9SAndroid Build Coastguard Worker // 18*d57664e9SAndroid Build Coastguard Worker // General-purpose Zip archive access. This class allows both reading and 19*d57664e9SAndroid Build Coastguard Worker // writing to Zip archives, including deletion of existing entries. 20*d57664e9SAndroid Build Coastguard Worker // 21*d57664e9SAndroid Build Coastguard Worker #ifndef __LIBS_ZIPFILE_H 22*d57664e9SAndroid Build Coastguard Worker #define __LIBS_ZIPFILE_H 23*d57664e9SAndroid Build Coastguard Worker 24*d57664e9SAndroid Build Coastguard Worker #include <utils/Vector.h> 25*d57664e9SAndroid Build Coastguard Worker #include <utils/Errors.h> 26*d57664e9SAndroid Build Coastguard Worker #include <stdio.h> 27*d57664e9SAndroid Build Coastguard Worker 28*d57664e9SAndroid Build Coastguard Worker #include "ZipEntry.h" 29*d57664e9SAndroid Build Coastguard Worker 30*d57664e9SAndroid Build Coastguard Worker namespace android { 31*d57664e9SAndroid Build Coastguard Worker 32*d57664e9SAndroid Build Coastguard Worker /* 33*d57664e9SAndroid Build Coastguard Worker * Manipulate a Zip archive. 34*d57664e9SAndroid Build Coastguard Worker * 35*d57664e9SAndroid Build Coastguard Worker * Some changes will not be visible in the until until "flush" is called. 36*d57664e9SAndroid Build Coastguard Worker * 37*d57664e9SAndroid Build Coastguard Worker * The correct way to update a file archive is to make all changes to a 38*d57664e9SAndroid Build Coastguard Worker * copy of the archive in a temporary file, and then unlink/rename over 39*d57664e9SAndroid Build Coastguard Worker * the original after everything completes. Because we're only interested 40*d57664e9SAndroid Build Coastguard Worker * in using this for packaging, we don't worry about such things. Crashing 41*d57664e9SAndroid Build Coastguard Worker * after making changes and before flush() completes could leave us with 42*d57664e9SAndroid Build Coastguard Worker * an unusable Zip archive. 43*d57664e9SAndroid Build Coastguard Worker */ 44*d57664e9SAndroid Build Coastguard Worker class ZipFile { 45*d57664e9SAndroid Build Coastguard Worker public: ZipFile(void)46*d57664e9SAndroid Build Coastguard Worker ZipFile(void) 47*d57664e9SAndroid Build Coastguard Worker : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) 48*d57664e9SAndroid Build Coastguard Worker {} ~ZipFile(void)49*d57664e9SAndroid Build Coastguard Worker ~ZipFile(void) { 50*d57664e9SAndroid Build Coastguard Worker if (!mReadOnly) 51*d57664e9SAndroid Build Coastguard Worker flush(); 52*d57664e9SAndroid Build Coastguard Worker if (mZipFp != NULL) 53*d57664e9SAndroid Build Coastguard Worker fclose(mZipFp); 54*d57664e9SAndroid Build Coastguard Worker discardEntries(); 55*d57664e9SAndroid Build Coastguard Worker } 56*d57664e9SAndroid Build Coastguard Worker 57*d57664e9SAndroid Build Coastguard Worker /* 58*d57664e9SAndroid Build Coastguard Worker * Open a new or existing archive. 59*d57664e9SAndroid Build Coastguard Worker */ 60*d57664e9SAndroid Build Coastguard Worker enum { 61*d57664e9SAndroid Build Coastguard Worker kOpenReadOnly = 0x01, 62*d57664e9SAndroid Build Coastguard Worker kOpenReadWrite = 0x02, 63*d57664e9SAndroid Build Coastguard Worker kOpenCreate = 0x04, // create if it doesn't exist 64*d57664e9SAndroid Build Coastguard Worker kOpenTruncate = 0x08, // if it exists, empty it 65*d57664e9SAndroid Build Coastguard Worker }; 66*d57664e9SAndroid Build Coastguard Worker status_t open(const char* zipFileName, int flags); 67*d57664e9SAndroid Build Coastguard Worker 68*d57664e9SAndroid Build Coastguard Worker /* 69*d57664e9SAndroid Build Coastguard Worker * Add a file to the end of the archive. Specify whether you want the 70*d57664e9SAndroid Build Coastguard Worker * library to try to store it compressed. 71*d57664e9SAndroid Build Coastguard Worker * 72*d57664e9SAndroid Build Coastguard Worker * If "storageName" is specified, the archive will use that instead 73*d57664e9SAndroid Build Coastguard Worker * of "fileName". 74*d57664e9SAndroid Build Coastguard Worker * 75*d57664e9SAndroid Build Coastguard Worker * If there is already an entry with the same name, the call fails. 76*d57664e9SAndroid Build Coastguard Worker * Existing entries with the same name must be removed first. 77*d57664e9SAndroid Build Coastguard Worker * 78*d57664e9SAndroid Build Coastguard Worker * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 79*d57664e9SAndroid Build Coastguard Worker */ add(const char * fileName,int compressionMethod,ZipEntry ** ppEntry)80*d57664e9SAndroid Build Coastguard Worker status_t add(const char* fileName, int compressionMethod, 81*d57664e9SAndroid Build Coastguard Worker ZipEntry** ppEntry) 82*d57664e9SAndroid Build Coastguard Worker { 83*d57664e9SAndroid Build Coastguard Worker return add(fileName, fileName, compressionMethod, ppEntry); 84*d57664e9SAndroid Build Coastguard Worker } add(const char * fileName,const char * storageName,int compressionMethod,ZipEntry ** ppEntry)85*d57664e9SAndroid Build Coastguard Worker status_t add(const char* fileName, const char* storageName, 86*d57664e9SAndroid Build Coastguard Worker int compressionMethod, ZipEntry** ppEntry) 87*d57664e9SAndroid Build Coastguard Worker { 88*d57664e9SAndroid Build Coastguard Worker return addCommon(fileName, NULL, 0, storageName, 89*d57664e9SAndroid Build Coastguard Worker ZipEntry::kCompressStored, 90*d57664e9SAndroid Build Coastguard Worker compressionMethod, ppEntry); 91*d57664e9SAndroid Build Coastguard Worker } 92*d57664e9SAndroid Build Coastguard Worker 93*d57664e9SAndroid Build Coastguard Worker /* 94*d57664e9SAndroid Build Coastguard Worker * Add a file that is already compressed with gzip. 95*d57664e9SAndroid Build Coastguard Worker * 96*d57664e9SAndroid Build Coastguard Worker * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 97*d57664e9SAndroid Build Coastguard Worker */ addGzip(const char * fileName,const char * storageName,ZipEntry ** ppEntry)98*d57664e9SAndroid Build Coastguard Worker status_t addGzip(const char* fileName, const char* storageName, 99*d57664e9SAndroid Build Coastguard Worker ZipEntry** ppEntry) 100*d57664e9SAndroid Build Coastguard Worker { 101*d57664e9SAndroid Build Coastguard Worker return addCommon(fileName, NULL, 0, storageName, 102*d57664e9SAndroid Build Coastguard Worker ZipEntry::kCompressDeflated, 103*d57664e9SAndroid Build Coastguard Worker ZipEntry::kCompressDeflated, ppEntry); 104*d57664e9SAndroid Build Coastguard Worker } 105*d57664e9SAndroid Build Coastguard Worker 106*d57664e9SAndroid Build Coastguard Worker /* 107*d57664e9SAndroid Build Coastguard Worker * Add a file from an in-memory data buffer. 108*d57664e9SAndroid Build Coastguard Worker * 109*d57664e9SAndroid Build Coastguard Worker * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 110*d57664e9SAndroid Build Coastguard Worker */ add(const void * data,size_t size,const char * storageName,int compressionMethod,ZipEntry ** ppEntry)111*d57664e9SAndroid Build Coastguard Worker status_t add(const void* data, size_t size, const char* storageName, 112*d57664e9SAndroid Build Coastguard Worker int compressionMethod, ZipEntry** ppEntry) 113*d57664e9SAndroid Build Coastguard Worker { 114*d57664e9SAndroid Build Coastguard Worker return addCommon(NULL, data, size, storageName, 115*d57664e9SAndroid Build Coastguard Worker ZipEntry::kCompressStored, 116*d57664e9SAndroid Build Coastguard Worker compressionMethod, ppEntry); 117*d57664e9SAndroid Build Coastguard Worker } 118*d57664e9SAndroid Build Coastguard Worker 119*d57664e9SAndroid Build Coastguard Worker /* 120*d57664e9SAndroid Build Coastguard Worker * Add an entry by copying it from another zip file. If "padding" is 121*d57664e9SAndroid Build Coastguard Worker * nonzero, the specified number of bytes will be added to the "extra" 122*d57664e9SAndroid Build Coastguard Worker * field in the header. 123*d57664e9SAndroid Build Coastguard Worker * 124*d57664e9SAndroid Build Coastguard Worker * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 125*d57664e9SAndroid Build Coastguard Worker */ 126*d57664e9SAndroid Build Coastguard Worker status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, 127*d57664e9SAndroid Build Coastguard Worker int padding, ZipEntry** ppEntry); 128*d57664e9SAndroid Build Coastguard Worker 129*d57664e9SAndroid Build Coastguard Worker /* 130*d57664e9SAndroid Build Coastguard Worker * Mark an entry as having been removed. It is not actually deleted 131*d57664e9SAndroid Build Coastguard Worker * from the archive or our internal data structures until flush() is 132*d57664e9SAndroid Build Coastguard Worker * called. 133*d57664e9SAndroid Build Coastguard Worker */ 134*d57664e9SAndroid Build Coastguard Worker status_t remove(ZipEntry* pEntry); 135*d57664e9SAndroid Build Coastguard Worker 136*d57664e9SAndroid Build Coastguard Worker /* 137*d57664e9SAndroid Build Coastguard Worker * Flush changes. If mNeedCDRewrite is set, this writes the central dir. 138*d57664e9SAndroid Build Coastguard Worker */ 139*d57664e9SAndroid Build Coastguard Worker status_t flush(void); 140*d57664e9SAndroid Build Coastguard Worker 141*d57664e9SAndroid Build Coastguard Worker /* 142*d57664e9SAndroid Build Coastguard Worker * Expand the data into the buffer provided. The buffer must hold 143*d57664e9SAndroid Build Coastguard Worker * at least <uncompressed len> bytes. Variation expands directly 144*d57664e9SAndroid Build Coastguard Worker * to a file. 145*d57664e9SAndroid Build Coastguard Worker * 146*d57664e9SAndroid Build Coastguard Worker * Returns "false" if an error was encountered in the compressed data. 147*d57664e9SAndroid Build Coastguard Worker */ 148*d57664e9SAndroid Build Coastguard Worker //bool uncompress(const ZipEntry* pEntry, void* buf) const; 149*d57664e9SAndroid Build Coastguard Worker //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; 150*d57664e9SAndroid Build Coastguard Worker void* uncompress(const ZipEntry* pEntry); 151*d57664e9SAndroid Build Coastguard Worker 152*d57664e9SAndroid Build Coastguard Worker /* 153*d57664e9SAndroid Build Coastguard Worker * Get an entry, by name. Returns NULL if not found. 154*d57664e9SAndroid Build Coastguard Worker * 155*d57664e9SAndroid Build Coastguard Worker * Does not return entries pending deletion. 156*d57664e9SAndroid Build Coastguard Worker */ 157*d57664e9SAndroid Build Coastguard Worker ZipEntry* getEntryByName(const char* fileName) const; 158*d57664e9SAndroid Build Coastguard Worker 159*d57664e9SAndroid Build Coastguard Worker /* 160*d57664e9SAndroid Build Coastguard Worker * Get the Nth entry in the archive. 161*d57664e9SAndroid Build Coastguard Worker * 162*d57664e9SAndroid Build Coastguard Worker * This will return an entry that is pending deletion. 163*d57664e9SAndroid Build Coastguard Worker */ getNumEntries(void)164*d57664e9SAndroid Build Coastguard Worker int getNumEntries(void) const { return mEntries.size(); } 165*d57664e9SAndroid Build Coastguard Worker ZipEntry* getEntryByIndex(int idx) const; 166*d57664e9SAndroid Build Coastguard Worker 167*d57664e9SAndroid Build Coastguard Worker private: 168*d57664e9SAndroid Build Coastguard Worker /* these are private and not defined */ 169*d57664e9SAndroid Build Coastguard Worker ZipFile(const ZipFile& src); 170*d57664e9SAndroid Build Coastguard Worker ZipFile& operator=(const ZipFile& src); 171*d57664e9SAndroid Build Coastguard Worker 172*d57664e9SAndroid Build Coastguard Worker class EndOfCentralDir { 173*d57664e9SAndroid Build Coastguard Worker public: EndOfCentralDir(void)174*d57664e9SAndroid Build Coastguard Worker EndOfCentralDir(void) : 175*d57664e9SAndroid Build Coastguard Worker mDiskNumber(0), 176*d57664e9SAndroid Build Coastguard Worker mDiskWithCentralDir(0), 177*d57664e9SAndroid Build Coastguard Worker mNumEntries(0), 178*d57664e9SAndroid Build Coastguard Worker mTotalNumEntries(0), 179*d57664e9SAndroid Build Coastguard Worker mCentralDirSize(0), 180*d57664e9SAndroid Build Coastguard Worker mCentralDirOffset(0), 181*d57664e9SAndroid Build Coastguard Worker mCommentLen(0), 182*d57664e9SAndroid Build Coastguard Worker mComment(NULL) 183*d57664e9SAndroid Build Coastguard Worker {} ~EndOfCentralDir(void)184*d57664e9SAndroid Build Coastguard Worker virtual ~EndOfCentralDir(void) { 185*d57664e9SAndroid Build Coastguard Worker delete[] mComment; 186*d57664e9SAndroid Build Coastguard Worker } 187*d57664e9SAndroid Build Coastguard Worker 188*d57664e9SAndroid Build Coastguard Worker status_t readBuf(const unsigned char* buf, int len); 189*d57664e9SAndroid Build Coastguard Worker status_t write(FILE* fp); 190*d57664e9SAndroid Build Coastguard Worker 191*d57664e9SAndroid Build Coastguard Worker //unsigned long mSignature; 192*d57664e9SAndroid Build Coastguard Worker unsigned short mDiskNumber; 193*d57664e9SAndroid Build Coastguard Worker unsigned short mDiskWithCentralDir; 194*d57664e9SAndroid Build Coastguard Worker unsigned short mNumEntries; 195*d57664e9SAndroid Build Coastguard Worker unsigned short mTotalNumEntries; 196*d57664e9SAndroid Build Coastguard Worker unsigned long mCentralDirSize; 197*d57664e9SAndroid Build Coastguard Worker unsigned long mCentralDirOffset; // offset from first disk 198*d57664e9SAndroid Build Coastguard Worker unsigned short mCommentLen; 199*d57664e9SAndroid Build Coastguard Worker unsigned char* mComment; 200*d57664e9SAndroid Build Coastguard Worker 201*d57664e9SAndroid Build Coastguard Worker enum { 202*d57664e9SAndroid Build Coastguard Worker kSignature = 0x06054b50, 203*d57664e9SAndroid Build Coastguard Worker kEOCDLen = 22, // EndOfCentralDir len, excl. comment 204*d57664e9SAndroid Build Coastguard Worker 205*d57664e9SAndroid Build Coastguard Worker kMaxCommentLen = 65535, // longest possible in ushort 206*d57664e9SAndroid Build Coastguard Worker kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, 207*d57664e9SAndroid Build Coastguard Worker 208*d57664e9SAndroid Build Coastguard Worker }; 209*d57664e9SAndroid Build Coastguard Worker 210*d57664e9SAndroid Build Coastguard Worker void dump(void) const; 211*d57664e9SAndroid Build Coastguard Worker }; 212*d57664e9SAndroid Build Coastguard Worker 213*d57664e9SAndroid Build Coastguard Worker 214*d57664e9SAndroid Build Coastguard Worker /* read all entries in the central dir */ 215*d57664e9SAndroid Build Coastguard Worker status_t readCentralDir(void); 216*d57664e9SAndroid Build Coastguard Worker 217*d57664e9SAndroid Build Coastguard Worker /* crunch deleted entries out */ 218*d57664e9SAndroid Build Coastguard Worker status_t crunchArchive(void); 219*d57664e9SAndroid Build Coastguard Worker 220*d57664e9SAndroid Build Coastguard Worker /* clean up mEntries */ 221*d57664e9SAndroid Build Coastguard Worker void discardEntries(void); 222*d57664e9SAndroid Build Coastguard Worker 223*d57664e9SAndroid Build Coastguard Worker /* common handler for all "add" functions */ 224*d57664e9SAndroid Build Coastguard Worker status_t addCommon(const char* fileName, const void* data, size_t size, 225*d57664e9SAndroid Build Coastguard Worker const char* storageName, int sourceType, int compressionMethod, 226*d57664e9SAndroid Build Coastguard Worker ZipEntry** ppEntry); 227*d57664e9SAndroid Build Coastguard Worker 228*d57664e9SAndroid Build Coastguard Worker /* copy all of "srcFp" into "dstFp" */ 229*d57664e9SAndroid Build Coastguard Worker status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); 230*d57664e9SAndroid Build Coastguard Worker /* copy all of "data" into "dstFp" */ 231*d57664e9SAndroid Build Coastguard Worker status_t copyDataToFp(FILE* dstFp, 232*d57664e9SAndroid Build Coastguard Worker const void* data, size_t size, unsigned long* pCRC32); 233*d57664e9SAndroid Build Coastguard Worker /* copy some of "srcFp" into "dstFp" */ 234*d57664e9SAndroid Build Coastguard Worker status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, 235*d57664e9SAndroid Build Coastguard Worker unsigned long* pCRC32); 236*d57664e9SAndroid Build Coastguard Worker /* like memmove(), but on parts of a single file */ 237*d57664e9SAndroid Build Coastguard Worker status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); 238*d57664e9SAndroid Build Coastguard Worker /* compress all of "srcFp" into "dstFp", using Deflate */ 239*d57664e9SAndroid Build Coastguard Worker status_t compressFpToFp(FILE* dstFp, FILE* srcFp, 240*d57664e9SAndroid Build Coastguard Worker const void* data, size_t size, unsigned long* pCRC32); 241*d57664e9SAndroid Build Coastguard Worker 242*d57664e9SAndroid Build Coastguard Worker /* get modification date from a file descriptor */ 243*d57664e9SAndroid Build Coastguard Worker time_t getModTime(int fd); 244*d57664e9SAndroid Build Coastguard Worker 245*d57664e9SAndroid Build Coastguard Worker /* 246*d57664e9SAndroid Build Coastguard Worker * We use stdio FILE*, which gives us buffering but makes dealing 247*d57664e9SAndroid Build Coastguard Worker * with files >2GB awkward. Until we support Zip64, we're fine. 248*d57664e9SAndroid Build Coastguard Worker */ 249*d57664e9SAndroid Build Coastguard Worker FILE* mZipFp; // Zip file pointer 250*d57664e9SAndroid Build Coastguard Worker 251*d57664e9SAndroid Build Coastguard Worker /* one of these per file */ 252*d57664e9SAndroid Build Coastguard Worker EndOfCentralDir mEOCD; 253*d57664e9SAndroid Build Coastguard Worker 254*d57664e9SAndroid Build Coastguard Worker /* did we open this read-only? */ 255*d57664e9SAndroid Build Coastguard Worker bool mReadOnly; 256*d57664e9SAndroid Build Coastguard Worker 257*d57664e9SAndroid Build Coastguard Worker /* set this when we trash the central dir */ 258*d57664e9SAndroid Build Coastguard Worker bool mNeedCDRewrite; 259*d57664e9SAndroid Build Coastguard Worker 260*d57664e9SAndroid Build Coastguard Worker /* 261*d57664e9SAndroid Build Coastguard Worker * One ZipEntry per entry in the zip file. I'm using pointers instead 262*d57664e9SAndroid Build Coastguard Worker * of objects because it's easier than making operator= work for the 263*d57664e9SAndroid Build Coastguard Worker * classes and sub-classes. 264*d57664e9SAndroid Build Coastguard Worker */ 265*d57664e9SAndroid Build Coastguard Worker Vector<ZipEntry*> mEntries; 266*d57664e9SAndroid Build Coastguard Worker }; 267*d57664e9SAndroid Build Coastguard Worker 268*d57664e9SAndroid Build Coastguard Worker }; // namespace android 269*d57664e9SAndroid Build Coastguard Worker 270*d57664e9SAndroid Build Coastguard Worker #endif // __LIBS_ZIPFILE_H 271