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