]>
Commit | Line | Data |
---|---|---|
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: | |
46 | ZipFile(void) | |
47 | : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) | |
48 | {} | |
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 | typedef 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 | */ | |
80 | status_t add(const char* fileName, int compressionMethod, | |
81 | ZipEntry** ppEntry) | |
82 | { | |
83 | return add(fileName, fileName, compressionMethod, ppEntry); | |
84 | } | |
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 | */ | |
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 | */ | |
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 | * 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 | ||
167 | private: | |
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 |