blob: 854f981a320bd9ba89753ca6efc113455daf0011 [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 Willemsenb589ae42015-10-29 21:26:18 +000080 status_t add(const char* fileName, int compressionMethod,
Mathias Agopian3344b2e2009-06-05 14:55:48 -070081 ZipEntry** ppEntry)
82 {
Dan Willemsenb589ae42015-10-29 21:26:18 +000083 return add(fileName, fileName, compressionMethod, ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -070084 }
85 status_t add(const char* fileName, const char* storageName,
Dan Willemsenb589ae42015-10-29 21:26:18 +000086 int compressionMethod, ZipEntry** ppEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -070087 {
88 return addCommon(fileName, NULL, 0, storageName,
Dan Willemsenb589ae42015-10-29 21:26:18 +000089 compressionMethod, ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -070090 }
91
92 /*
Mathias Agopian3344b2e2009-06-05 14:55:48 -070093 * Add a file from an in-memory data buffer.
94 *
95 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
96 */
97 status_t add(const void* data, size_t size, const char* storageName,
Dan Willemsenb589ae42015-10-29 21:26:18 +000098 int compressionMethod, ZipEntry** ppEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -070099 {
100 return addCommon(NULL, data, size, storageName,
Dan Willemsenb589ae42015-10-29 21:26:18 +0000101 compressionMethod, ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700102 }
103
104 /*
Fabien Sanglarda7206352020-10-20 15:47:10 -0700105 * Add an entry by copying it from another zip file. If "alignment" is
106 * nonzero, an appropriate number of bytes will be added to the "extra"
107 * field in the header so the entry payload is aligned.
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700108 *
109 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
110 */
111 status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
Fabien Sanglarda7206352020-10-20 15:47:10 -0700112 int alignment, ZipEntry** ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700113
114 /*
Raph Levien093d04c2014-07-07 16:00:29 -0700115 * Add an entry by copying it from another zip file, recompressing with
116 * Zopfli if already compressed.
117 *
118 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
119 */
120 status_t addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
Dan Willemsenb589ae42015-10-29 21:26:18 +0000121 ZipEntry** ppEntry);
Raph Levien093d04c2014-07-07 16:00:29 -0700122
123 /*
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700124 * Mark an entry as having been removed. It is not actually deleted
125 * from the archive or our internal data structures until flush() is
126 * called.
127 */
128 status_t remove(ZipEntry* pEntry);
129
130 /*
131 * Flush changes. If mNeedCDRewrite is set, this writes the central dir.
132 */
133 status_t flush(void);
134
135 /*
136 * Expand the data into the buffer provided. The buffer must hold
137 * at least <uncompressed len> bytes. Variation expands directly
138 * to a file.
139 *
140 * Returns "false" if an error was encountered in the compressed data.
141 */
142 //bool uncompress(const ZipEntry* pEntry, void* buf) const;
143 //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
Raph Levien093d04c2014-07-07 16:00:29 -0700144 void* uncompress(const ZipEntry* pEntry) const;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700145
146 /*
147 * Get an entry, by name. Returns NULL if not found.
148 *
149 * Does not return entries pending deletion.
150 */
151 ZipEntry* getEntryByName(const char* fileName) const;
152
153 /*
154 * Get the Nth entry in the archive.
155 *
156 * This will return an entry that is pending deletion.
157 */
158 int getNumEntries(void) const { return mEntries.size(); }
159 ZipEntry* getEntryByIndex(int idx) const;
160
161private:
162 /* these are private and not defined */
163 ZipFile(const ZipFile& src);
164 ZipFile& operator=(const ZipFile& src);
165
Fabien Sanglarda7206352020-10-20 15:47:10 -0700166 status_t alignEntry(android::ZipEntry* pEntry, uint32_t alignTo);
167
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700168 class EndOfCentralDir {
169 public:
170 EndOfCentralDir(void) :
171 mDiskNumber(0),
172 mDiskWithCentralDir(0),
173 mNumEntries(0),
174 mTotalNumEntries(0),
175 mCentralDirSize(0),
176 mCentralDirOffset(0),
177 mCommentLen(0),
178 mComment(NULL)
179 {}
180 virtual ~EndOfCentralDir(void) {
181 delete[] mComment;
182 }
183
Dan Willemsen41bc4242015-11-04 14:08:20 -0800184 status_t readBuf(const uint8_t* buf, int len);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700185 status_t write(FILE* fp);
186
Dan Willemsen41bc4242015-11-04 14:08:20 -0800187 //uint32_t mSignature;
188 uint16_t mDiskNumber;
189 uint16_t mDiskWithCentralDir;
190 uint16_t mNumEntries;
191 uint16_t mTotalNumEntries;
192 uint32_t mCentralDirSize;
193 uint32_t mCentralDirOffset; // offset from first disk
194 uint16_t mCommentLen;
195 uint8_t* mComment;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700196
197 enum {
198 kSignature = 0x06054b50,
199 kEOCDLen = 22, // EndOfCentralDir len, excl. comment
200
201 kMaxCommentLen = 65535, // longest possible in ushort
202 kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
203
204 };
205
206 void dump(void) const;
207 };
208
209
210 /* read all entries in the central dir */
211 status_t readCentralDir(void);
212
213 /* crunch deleted entries out */
214 status_t crunchArchive(void);
215
216 /* clean up mEntries */
217 void discardEntries(void);
218
219 /* common handler for all "add" functions */
220 status_t addCommon(const char* fileName, const void* data, size_t size,
Narayan Kamathb46507f2017-02-16 10:53:09 +0000221 const char* storageName, int compressionMethod, ZipEntry** ppEntry);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700222
223 /* copy all of "srcFp" into "dstFp" */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800224 status_t copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700225 /* copy all of "data" into "dstFp" */
226 status_t copyDataToFp(FILE* dstFp,
Dan Willemsen41bc4242015-11-04 14:08:20 -0800227 const void* data, size_t size, uint32_t* pCRC32);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700228 /* copy some of "srcFp" into "dstFp" */
Chih-Hung Hsieh0c0d9282018-08-10 15:14:26 -0700229 status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, size_t length,
Dan Willemsen41bc4242015-11-04 14:08:20 -0800230 uint32_t* pCRC32);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700231 /* like memmove(), but on parts of a single file */
232 status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
233 /* compress all of "srcFp" into "dstFp", using Deflate */
234 status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
Dan Willemsen41bc4242015-11-04 14:08:20 -0800235 const void* data, size_t size, uint32_t* pCRC32);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700236
237 /* get modification date from a file descriptor */
238 time_t getModTime(int fd);
239
240 /*
241 * We use stdio FILE*, which gives us buffering but makes dealing
242 * with files >2GB awkward. Until we support Zip64, we're fine.
243 */
244 FILE* mZipFp; // Zip file pointer
245
246 /* one of these per file */
247 EndOfCentralDir mEOCD;
248
249 /* did we open this read-only? */
250 bool mReadOnly;
251
252 /* set this when we trash the central dir */
253 bool mNeedCDRewrite;
254
255 /*
256 * One ZipEntry per entry in the zip file. I'm using pointers instead
257 * of objects because it's easier than making operator= work for the
258 * classes and sub-classes.
259 */
260 Vector<ZipEntry*> mEntries;
261};
262
263}; // namespace android
264
265#endif // __LIBS_ZIPFILE_H