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