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 "BigBuffer.h"
25 #include "ZipEntry.h"
26 
27 #include <stdio.h>
28 #include <utils/Errors.h>
29 #include <vector>
30 
31 namespace aapt {
32 
33 using 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  */
47 class ZipFile {
48 public:
ZipFile(void)49     ZipFile(void)
50       : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
51       {}
~ZipFile(void)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      */
add(const char * fileName,int compressionMethod,ZipEntry ** ppEntry)83     status_t add(const char* fileName, int compressionMethod,
84         ZipEntry** ppEntry)
85     {
86         return add(fileName, fileName, compressionMethod, ppEntry);
87     }
add(const char * fileName,const char * storageName,int compressionMethod,ZipEntry ** ppEntry)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      */
addGzip(const char * fileName,const char * storageName,ZipEntry ** ppEntry)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      */
add(const void * data,size_t size,const char * storageName,int compressionMethod,ZipEntry ** ppEntry)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     /*
126      * 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
129      * 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,
135                  const char* storageName, int padding, ZipEntry** ppEntry);
136 
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      */
getNumEntries(void)172     int getNumEntries(void) const { return mEntries.size(); }
173     ZipEntry* getEntryByIndex(int idx) const;
174 
175 private:
176     /* these are private and not defined */
177     ZipFile(const ZipFile& src);
178     ZipFile& operator=(const ZipFile& src);
179 
180     class EndOfCentralDir {
181     public:
EndOfCentralDir(void)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             {}
~EndOfCentralDir(void)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
279