18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* 28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (C) 2006 The Android Open Source Project 38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Licensed under the Apache License, Version 2.0 (the "License"); 5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * you may not use this file except in compliance with the License. 6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * You may obtain a copy of the License at 78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * http://www.apache.org/licenses/LICENSE-2.0 98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Unless required by applicable law or agreed to in writing, software 118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * distributed under the License is distributed on an "AS IS" BASIS, 128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * See the License for the specific language governing permissions and 148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * limitations under the License. 158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// 188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// General-purpose Zip archive access. This class allows both reading and 198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// writing to Zip archives, including deletion of existing entries. 208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// 218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#ifndef __LIBS_ZIPFILE_H 228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define __LIBS_ZIPFILE_H 238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <utils/Vector.h> 258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <utils/Errors.h> 268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <stdio.h> 278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "ZipEntry.h" 298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtnamespace android { 318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* 338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Manipulate a Zip archive. 348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Some changes will not be visible in the until until "flush" is called. 368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * The correct way to update a file archive is to make all changes to a 388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * copy of the archive in a temporary file, and then unlink/rename over 398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * the original after everything completes. Because we're only interested 408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * in using this for packaging, we don't worry about such things. Crashing 418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * after making changes and before flush() completes could leave us with 428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * an unusable Zip archive. 438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtclass ZipFile { 458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtpublic: 468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ZipFile(void) 478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) 488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt {} 498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ~ZipFile(void) { 508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!mReadOnly) 518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt flush(); 528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (mZipFp != NULL) 538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt fclose(mZipFp); 548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt discardEntries(); 558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Open a new or existing archive. 598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt enum { 618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt kOpenReadOnly = 0x01, 628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt kOpenReadWrite = 0x02, 638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt kOpenCreate = 0x04, // create if it doesn't exist 648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt kOpenTruncate = 0x08, // if it exists, empty it 658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt }; 668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt status_t open(const char* zipFileName, int flags); 678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Add a file to the end of the archive. Specify whether you want the 708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * library to try to store it compressed. 718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * If "storageName" is specified, the archive will use that instead 738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * of "fileName". 748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * If there is already an entry with the same name, the call fails. 768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Existing entries with the same name must be removed first. 778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt status_t add(const char* fileName, int compressionMethod, 818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ZipEntry** ppEntry) 828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt { 838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return add(fileName, fileName, compressionMethod, ppEntry); 848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt status_t add(const char* fileName, const char* storageName, 868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int compressionMethod, ZipEntry** ppEntry) 878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt { 888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return addCommon(fileName, NULL, 0, storageName, 898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ZipEntry::kCompressStored, 908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt compressionMethod, ppEntry); 918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Add a file that is already compressed with gzip. 958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt status_t addGzip(const char* fileName, const char* storageName, 99a38abf9af7bec7e89dbfb39ac7bb77223fe47c72Dmitry Shmidt ZipEntry** ppEntry) 100a38abf9af7bec7e89dbfb39ac7bb77223fe47c72Dmitry Shmidt { 1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return addCommon(fileName, NULL, 0, storageName, 1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ZipEntry::kCompressDeflated, 1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ZipEntry::kCompressDeflated, ppEntry); 1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Add a file from an in-memory data buffer. 1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt status_t add(const void* data, size_t size, const char* storageName, 1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt int compressionMethod, ZipEntry** ppEntry) 1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt { 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 * Mark an entry as having been removed. It is not actually deleted 131 * from the archive or our internal data structures until flush() is 132 * called. 133 */ 134 status_t remove(ZipEntry* pEntry); 135 136 /* 137 * Flush changes. If mNeedCDRewrite is set, this writes the central dir. 138 */ 139 status_t flush(void); 140 141 /* 142 * Expand the data into the buffer provided. The buffer must hold 143 * at least <uncompressed len> bytes. Variation expands directly 144 * to a file. 145 * 146 * Returns "false" if an error was encountered in the compressed data. 147 */ 148 //bool uncompress(const ZipEntry* pEntry, void* buf) const; 149 //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; 150 void* uncompress(const ZipEntry* pEntry); 151 152 /* 153 * Get an entry, by name. Returns NULL if not found. 154 * 155 * Does not return entries pending deletion. 156 */ 157 ZipEntry* getEntryByName(const char* fileName) const; 158 159 /* 160 * Get the Nth entry in the archive. 161 * 162 * This will return an entry that is pending deletion. 163 */ 164 int getNumEntries(void) const { return mEntries.size(); } 165 ZipEntry* getEntryByIndex(int idx) const; 166 167private: 168 /* these are private and not defined */ 169 ZipFile(const ZipFile& src); 170 ZipFile& operator=(const ZipFile& src); 171 172 class EndOfCentralDir { 173 public: 174 EndOfCentralDir(void) : 175 mDiskNumber(0), 176 mDiskWithCentralDir(0), 177 mNumEntries(0), 178 mTotalNumEntries(0), 179 mCentralDirSize(0), 180 mCentralDirOffset(0), 181 mCommentLen(0), 182 mComment(NULL) 183 {} 184 virtual ~EndOfCentralDir(void) { 185 delete[] mComment; 186 } 187 188 status_t readBuf(const unsigned char* buf, int len); 189 status_t write(FILE* fp); 190 191 //unsigned long mSignature; 192 unsigned short mDiskNumber; 193 unsigned short mDiskWithCentralDir; 194 unsigned short mNumEntries; 195 unsigned short mTotalNumEntries; 196 unsigned long mCentralDirSize; 197 unsigned long mCentralDirOffset; // offset from first disk 198 unsigned short mCommentLen; 199 unsigned char* mComment; 200 201 enum { 202 kSignature = 0x06054b50, 203 kEOCDLen = 22, // EndOfCentralDir len, excl. comment 204 205 kMaxCommentLen = 65535, // longest possible in ushort 206 kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, 207 208 }; 209 210 void dump(void) const; 211 }; 212 213 214 /* read all entries in the central dir */ 215 status_t readCentralDir(void); 216 217 /* crunch deleted entries out */ 218 status_t crunchArchive(void); 219 220 /* clean up mEntries */ 221 void discardEntries(void); 222 223 /* common handler for all "add" functions */ 224 status_t addCommon(const char* fileName, const void* data, size_t size, 225 const char* storageName, int sourceType, int compressionMethod, 226 ZipEntry** ppEntry); 227 228 /* copy all of "srcFp" into "dstFp" */ 229 status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); 230 /* copy all of "data" into "dstFp" */ 231 status_t copyDataToFp(FILE* dstFp, 232 const void* data, size_t size, unsigned long* pCRC32); 233 /* copy some of "srcFp" into "dstFp" */ 234 status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, 235 unsigned long* pCRC32); 236 /* like memmove(), but on parts of a single file */ 237 status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); 238 /* compress all of "srcFp" into "dstFp", using Deflate */ 239 status_t compressFpToFp(FILE* dstFp, FILE* srcFp, 240 const void* data, size_t size, unsigned long* pCRC32); 241 242 /* get modification date from a file descriptor */ 243 time_t getModTime(int fd); 244 245 /* 246 * We use stdio FILE*, which gives us buffering but makes dealing 247 * with files >2GB awkward. Until we support Zip64, we're fine. 248 */ 249 FILE* mZipFp; // Zip file pointer 250 251 /* one of these per file */ 252 EndOfCentralDir mEOCD; 253 254 /* did we open this read-only? */ 255 bool mReadOnly; 256 257 /* set this when we trash the central dir */ 258 bool mNeedCDRewrite; 259 260 /* 261 * One ZipEntry per entry in the zip file. I'm using pointers instead 262 * of objects because it's easier than making operator= work for the 263 * classes and sub-classes. 264 */ 265 Vector<ZipEntry*> mEntries; 266}; 267 268}; // namespace android 269 270#endif // __LIBS_ZIPFILE_H 271