blob: 787576fc425be6b77c4b72068b34d940083a7159 [file] [log] [blame]
Mathias Agopian3344b2e2009-06-05 14:55:48 -07001/*
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
30namespace 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 */
44class ZipFile {
45public:
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 */
Glenn Kasten1022e272012-01-12 07:30:27 -080060 enum {
Mathias Agopian3344b2e2009-06-05 14:55:48 -070061 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 */
Dan Willemsen3c2c0642015-10-27 16:25:29 -070080 status_t add(const char* fileName, int compressionMethod, bool removeTime,
Mathias Agopian3344b2e2009-06-05 14:55:48 -070081 ZipEntry** ppEntry)
82 {
Dan Willemsen3c2c0642015-10-27 16:25:29 -070083 return add(fileName, fileName, compressionMethod, removeTime, ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -070084 }
85 status_t add(const char* fileName, const char* storageName,
Dan Willemsen3c2c0642015-10-27 16:25:29 -070086 int compressionMethod, bool removeTime, ZipEntry** ppEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -070087 {
88 return addCommon(fileName, NULL, 0, storageName,
89 ZipEntry::kCompressStored,
Dan Willemsen3c2c0642015-10-27 16:25:29 -070090 compressionMethod, removeTime, ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -070091 }
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,
Dan Willemsen3c2c0642015-10-27 16:25:29 -070099 bool removeTime, ZipEntry** ppEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700100 {
101 return addCommon(fileName, NULL, 0, storageName,
102 ZipEntry::kCompressDeflated,
Dan Willemsen3c2c0642015-10-27 16:25:29 -0700103 ZipEntry::kCompressDeflated,
104 removeTime, ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700105 }
106
107 /*
108 * Add a file from an in-memory data buffer.
109 *
110 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
111 */
112 status_t add(const void* data, size_t size, const char* storageName,
Dan Willemsen3c2c0642015-10-27 16:25:29 -0700113 int compressionMethod, bool removeTime, ZipEntry** ppEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700114 {
115 return addCommon(NULL, data, size, storageName,
116 ZipEntry::kCompressStored,
Dan Willemsen3c2c0642015-10-27 16:25:29 -0700117 compressionMethod, removeTime, ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700118 }
119
120 /*
121 * Add an entry by copying it from another zip file. If "padding" is
122 * nonzero, the specified number of bytes will be added to the "extra"
123 * field in the header.
124 *
125 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
126 */
127 status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
Dan Willemsen3c2c0642015-10-27 16:25:29 -0700128 int padding, bool removeTime, ZipEntry** ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700129
130 /*
Raph Levien093d04c2014-07-07 16:00:29 -0700131 * Add an entry by copying it from another zip file, recompressing with
132 * Zopfli if already compressed.
133 *
134 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
135 */
136 status_t addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
Dan Willemsen3c2c0642015-10-27 16:25:29 -0700137 bool removeTime, ZipEntry** ppEntry);
Raph Levien093d04c2014-07-07 16:00:29 -0700138
139 /*
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700140 * Mark an entry as having been removed. It is not actually deleted
141 * from the archive or our internal data structures until flush() is
142 * called.
143 */
144 status_t remove(ZipEntry* pEntry);
145
146 /*
147 * Flush changes. If mNeedCDRewrite is set, this writes the central dir.
148 */
149 status_t flush(void);
150
151 /*
152 * Expand the data into the buffer provided. The buffer must hold
153 * at least <uncompressed len> bytes. Variation expands directly
154 * to a file.
155 *
156 * Returns "false" if an error was encountered in the compressed data.
157 */
158 //bool uncompress(const ZipEntry* pEntry, void* buf) const;
159 //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
Raph Levien093d04c2014-07-07 16:00:29 -0700160 void* uncompress(const ZipEntry* pEntry) const;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700161
162 /*
163 * Get an entry, by name. Returns NULL if not found.
164 *
165 * Does not return entries pending deletion.
166 */
167 ZipEntry* getEntryByName(const char* fileName) const;
168
169 /*
170 * Get the Nth entry in the archive.
171 *
172 * This will return an entry that is pending deletion.
173 */
174 int getNumEntries(void) const { return mEntries.size(); }
175 ZipEntry* getEntryByIndex(int idx) const;
176
177private:
178 /* these are private and not defined */
179 ZipFile(const ZipFile& src);
180 ZipFile& operator=(const ZipFile& src);
181
182 class EndOfCentralDir {
183 public:
184 EndOfCentralDir(void) :
185 mDiskNumber(0),
186 mDiskWithCentralDir(0),
187 mNumEntries(0),
188 mTotalNumEntries(0),
189 mCentralDirSize(0),
190 mCentralDirOffset(0),
191 mCommentLen(0),
192 mComment(NULL)
193 {}
194 virtual ~EndOfCentralDir(void) {
195 delete[] mComment;
196 }
197
198 status_t readBuf(const unsigned char* buf, int len);
199 status_t write(FILE* fp);
200
201 //unsigned long mSignature;
202 unsigned short mDiskNumber;
203 unsigned short mDiskWithCentralDir;
204 unsigned short mNumEntries;
205 unsigned short mTotalNumEntries;
206 unsigned long mCentralDirSize;
207 unsigned long mCentralDirOffset; // offset from first disk
208 unsigned short mCommentLen;
209 unsigned char* mComment;
210
211 enum {
212 kSignature = 0x06054b50,
213 kEOCDLen = 22, // EndOfCentralDir len, excl. comment
214
215 kMaxCommentLen = 65535, // longest possible in ushort
216 kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
217
218 };
219
220 void dump(void) const;
221 };
222
223
224 /* read all entries in the central dir */
225 status_t readCentralDir(void);
226
227 /* crunch deleted entries out */
228 status_t crunchArchive(void);
229
230 /* clean up mEntries */
231 void discardEntries(void);
232
233 /* common handler for all "add" functions */
234 status_t addCommon(const char* fileName, const void* data, size_t size,
235 const char* storageName, int sourceType, int compressionMethod,
Dan Willemsen3c2c0642015-10-27 16:25:29 -0700236 bool removeTime, ZipEntry** ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700237
238 /* copy all of "srcFp" into "dstFp" */
239 status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
240 /* copy all of "data" into "dstFp" */
241 status_t copyDataToFp(FILE* dstFp,
242 const void* data, size_t size, unsigned long* pCRC32);
243 /* copy some of "srcFp" into "dstFp" */
244 status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
245 unsigned long* pCRC32);
246 /* like memmove(), but on parts of a single file */
247 status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
248 /* compress all of "srcFp" into "dstFp", using Deflate */
249 status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
250 const void* data, size_t size, unsigned long* pCRC32);
251
252 /* get modification date from a file descriptor */
253 time_t getModTime(int fd);
254
255 /*
256 * We use stdio FILE*, which gives us buffering but makes dealing
257 * with files >2GB awkward. Until we support Zip64, we're fine.
258 */
259 FILE* mZipFp; // Zip file pointer
260
261 /* one of these per file */
262 EndOfCentralDir mEOCD;
263
264 /* did we open this read-only? */
265 bool mReadOnly;
266
267 /* set this when we trash the central dir */
268 bool mNeedCDRewrite;
269
270 /*
271 * One ZipEntry per entry in the zip file. I'm using pointers instead
272 * of objects because it's easier than making operator= work for the
273 * classes and sub-classes.
274 */
275 Vector<ZipEntry*> mEntries;
276};
277
278}; // namespace android
279
280#endif // __LIBS_ZIPFILE_H