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