ZipFile.h revision 3c2c064c8791ed2d4b9d6780d7b87f406ab78f10
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
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     */
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     */
80    status_t add(const char* fileName, int compressionMethod, bool removeTime,
81        ZipEntry** ppEntry)
82    {
83        return add(fileName, fileName, compressionMethod, removeTime, ppEntry);
84    }
85    status_t add(const char* fileName, const char* storageName,
86        int compressionMethod, bool removeTime, ZipEntry** ppEntry)
87    {
88        return addCommon(fileName, NULL, 0, storageName,
89                         ZipEntry::kCompressStored,
90                         compressionMethod, removeTime, 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     */
98    status_t addGzip(const char* fileName, const char* storageName,
99        bool removeTime, ZipEntry** ppEntry)
100    {
101        return addCommon(fileName, NULL, 0, storageName,
102                         ZipEntry::kCompressDeflated,
103                         ZipEntry::kCompressDeflated,
104                         removeTime, ppEntry);
105    }
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,
113        int compressionMethod, bool removeTime, ZipEntry** ppEntry)
114    {
115        return addCommon(NULL, data, size, storageName,
116                         ZipEntry::kCompressStored,
117                         compressionMethod, removeTime, ppEntry);
118    }
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,
128        int padding, bool removeTime, ZipEntry** ppEntry);
129
130    /*
131     * 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,
137        bool removeTime, ZipEntry** ppEntry);
138
139    /*
140     * 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;
160    void* uncompress(const ZipEntry* pEntry) const;
161
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,
236        bool removeTime, ZipEntry** ppEntry);
237
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
281