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:
ZipFile(void)46     ZipFile(void)
47       : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
48       {}
~ZipFile(void)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     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      */
add(const char * fileName,int compressionMethod,ZipEntry ** ppEntry)80     status_t add(const char* fileName, int compressionMethod,
81         ZipEntry** ppEntry)
82     {
83         return add(fileName, fileName, compressionMethod, ppEntry);
84     }
add(const char * fileName,const char * storageName,int compressionMethod,ZipEntry ** ppEntry)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      */
addGzip(const char * fileName,const char * storageName,ZipEntry ** ppEntry)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      */
add(const void * data,size_t size,const char * storageName,int compressionMethod,ZipEntry ** ppEntry)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      * Add an entry by copying it from another zip file, recompressing with
131      * Zopfli if already compressed.
132      *
133      * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
134      */
135     status_t addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
136         ZipEntry** ppEntry);
137 
138     /*
139      * Mark an entry as having been removed.  It is not actually deleted
140      * from the archive or our internal data structures until flush() is
141      * called.
142      */
143     status_t remove(ZipEntry* pEntry);
144 
145     /*
146      * Flush changes.  If mNeedCDRewrite is set, this writes the central dir.
147      */
148     status_t flush(void);
149 
150     /*
151      * Expand the data into the buffer provided.  The buffer must hold
152      * at least <uncompressed len> bytes.  Variation expands directly
153      * to a file.
154      *
155      * Returns "false" if an error was encountered in the compressed data.
156      */
157     //bool uncompress(const ZipEntry* pEntry, void* buf) const;
158     //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
159     void* uncompress(const ZipEntry* pEntry) const;
160 
161     /*
162      * Get an entry, by name.  Returns NULL if not found.
163      *
164      * Does not return entries pending deletion.
165      */
166     ZipEntry* getEntryByName(const char* fileName) const;
167 
168     /*
169      * Get the Nth entry in the archive.
170      *
171      * This will return an entry that is pending deletion.
172      */
getNumEntries(void)173     int getNumEntries(void) const { return mEntries.size(); }
174     ZipEntry* getEntryByIndex(int idx) const;
175 
176 private:
177     /* these are private and not defined */
178     ZipFile(const ZipFile& src);
179     ZipFile& operator=(const ZipFile& src);
180 
181     class EndOfCentralDir {
182     public:
EndOfCentralDir(void)183         EndOfCentralDir(void) :
184             mDiskNumber(0),
185             mDiskWithCentralDir(0),
186             mNumEntries(0),
187             mTotalNumEntries(0),
188             mCentralDirSize(0),
189             mCentralDirOffset(0),
190             mCommentLen(0),
191             mComment(NULL)
192             {}
~EndOfCentralDir(void)193         virtual ~EndOfCentralDir(void) {
194             delete[] mComment;
195         }
196 
197         status_t readBuf(const uint8_t* buf, int len);
198         status_t write(FILE* fp);
199 
200         //uint32_t mSignature;
201         uint16_t mDiskNumber;
202         uint16_t mDiskWithCentralDir;
203         uint16_t mNumEntries;
204         uint16_t mTotalNumEntries;
205         uint32_t mCentralDirSize;
206         uint32_t mCentralDirOffset;      // offset from first disk
207         uint16_t mCommentLen;
208         uint8_t* mComment;
209 
210         enum {
211             kSignature      = 0x06054b50,
212             kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment
213 
214             kMaxCommentLen  = 65535,    // longest possible in ushort
215             kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
216 
217         };
218 
219         void dump(void) const;
220     };
221 
222 
223     /* read all entries in the central dir */
224     status_t readCentralDir(void);
225 
226     /* crunch deleted entries out */
227     status_t crunchArchive(void);
228 
229     /* clean up mEntries */
230     void discardEntries(void);
231 
232     /* common handler for all "add" functions */
233     status_t addCommon(const char* fileName, const void* data, size_t size,
234         const char* storageName, int sourceType, int compressionMethod,
235         ZipEntry** ppEntry);
236 
237     /* copy all of "srcFp" into "dstFp" */
238     status_t copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32);
239     /* copy all of "data" into "dstFp" */
240     status_t copyDataToFp(FILE* dstFp,
241         const void* data, size_t size, uint32_t* pCRC32);
242     /* copy some of "srcFp" into "dstFp" */
243     status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
244         uint32_t* pCRC32);
245     /* like memmove(), but on parts of a single file */
246     status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
247     /* compress all of "srcFp" into "dstFp", using Deflate */
248     status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
249         const void* data, size_t size, uint32_t* pCRC32);
250 
251     /* get modification date from a file descriptor */
252     time_t getModTime(int fd);
253 
254     /*
255      * We use stdio FILE*, which gives us buffering but makes dealing
256      * with files >2GB awkward.  Until we support Zip64, we're fine.
257      */
258     FILE*           mZipFp;             // Zip file pointer
259 
260     /* one of these per file */
261     EndOfCentralDir mEOCD;
262 
263     /* did we open this read-only? */
264     bool            mReadOnly;
265 
266     /* set this when we trash the central dir */
267     bool            mNeedCDRewrite;
268 
269     /*
270      * One ZipEntry per entry in the zip file.  I'm using pointers instead
271      * of objects because it's easier than making operator= work for the
272      * classes and sub-classes.
273      */
274     Vector<ZipEntry*>   mEntries;
275 };
276 
277 }; // namespace android
278 
279 #endif // __LIBS_ZIPFILE_H
280