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