18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (C) 2006 The Android Open Source Project
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Licensed under the Apache License, Version 2.0 (the "License");
5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * you may not use this file except in compliance with the License.
6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * You may obtain a copy of the License at
78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *      http://www.apache.org/licenses/LICENSE-2.0
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Unless required by applicable law or agreed to in writing, software
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * distributed under the License is distributed on an "AS IS" BASIS,
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * See the License for the specific language governing permissions and
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * limitations under the License.
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt//
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// General-purpose Zip archive access.  This class allows both reading and
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// writing to Zip archives, including deletion of existing entries.
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt//
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#ifndef __LIBS_ZIPFILE_H
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define __LIBS_ZIPFILE_H
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <utils/Vector.h>
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <utils/Errors.h>
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <stdio.h>
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "ZipEntry.h"
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtnamespace android {
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Manipulate a Zip archive.
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Some changes will not be visible in the until until "flush" is called.
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * The correct way to update a file archive is to make all changes to a
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * copy of the archive in a temporary file, and then unlink/rename over
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * the original after everything completes.  Because we're only interested
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * in using this for packaging, we don't worry about such things.  Crashing
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * after making changes and before flush() completes could leave us with
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * an unusable Zip archive.
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtclass ZipFile {
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtpublic:
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    ZipFile(void)
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      {}
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    ~ZipFile(void) {
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (!mReadOnly)
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            flush();
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (mZipFp != NULL)
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            fclose(mZipFp);
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        discardEntries();
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    /*
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * Open a new or existing archive.
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     */
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    enum {
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        kOpenReadOnly   = 0x01,
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        kOpenReadWrite  = 0x02,
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        kOpenCreate     = 0x04,     // create if it doesn't exist
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        kOpenTruncate   = 0x08,     // if it exists, empty it
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    };
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    status_t open(const char* zipFileName, int flags);
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    /*
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * Add a file to the end of the archive.  Specify whether you want the
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * library to try to store it compressed.
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     *
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * If "storageName" is specified, the archive will use that instead
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * of "fileName".
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     *
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * If there is already an entry with the same name, the call fails.
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * Existing entries with the same name must be removed first.
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     *
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     */
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    status_t add(const char* fileName, int compressionMethod,
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        ZipEntry** ppEntry)
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    {
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return add(fileName, fileName, compressionMethod, ppEntry);
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    status_t add(const char* fileName, const char* storageName,
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        int compressionMethod, ZipEntry** ppEntry)
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    {
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return addCommon(fileName, NULL, 0, storageName,
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                         ZipEntry::kCompressStored,
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                         compressionMethod, ppEntry);
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    /*
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * Add a file that is already compressed with gzip.
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     *
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     */
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    status_t addGzip(const char* fileName, const char* storageName,
99a38abf9af7bec7e89dbfb39ac7bb77223fe47c72Dmitry Shmidt        ZipEntry** ppEntry)
100a38abf9af7bec7e89dbfb39ac7bb77223fe47c72Dmitry Shmidt    {
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return addCommon(fileName, NULL, 0, storageName,
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                         ZipEntry::kCompressDeflated,
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                         ZipEntry::kCompressDeflated, ppEntry);
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    /*
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * Add a file from an in-memory data buffer.
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     *
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt     */
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    status_t add(const void* data, size_t size, const char* storageName,
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        int compressionMethod, ZipEntry** ppEntry)
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    {
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     * Mark an entry as having been removed.  It is not actually deleted
131     * from the archive or our internal data structures until flush() is
132     * called.
133     */
134    status_t remove(ZipEntry* pEntry);
135
136    /*
137     * Flush changes.  If mNeedCDRewrite is set, this writes the central dir.
138     */
139    status_t flush(void);
140
141    /*
142     * Expand the data into the buffer provided.  The buffer must hold
143     * at least <uncompressed len> bytes.  Variation expands directly
144     * to a file.
145     *
146     * Returns "false" if an error was encountered in the compressed data.
147     */
148    //bool uncompress(const ZipEntry* pEntry, void* buf) const;
149    //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
150    void* uncompress(const ZipEntry* pEntry);
151
152    /*
153     * Get an entry, by name.  Returns NULL if not found.
154     *
155     * Does not return entries pending deletion.
156     */
157    ZipEntry* getEntryByName(const char* fileName) const;
158
159    /*
160     * Get the Nth entry in the archive.
161     *
162     * This will return an entry that is pending deletion.
163     */
164    int getNumEntries(void) const { return mEntries.size(); }
165    ZipEntry* getEntryByIndex(int idx) const;
166
167private:
168    /* these are private and not defined */
169    ZipFile(const ZipFile& src);
170    ZipFile& operator=(const ZipFile& src);
171
172    class EndOfCentralDir {
173    public:
174        EndOfCentralDir(void) :
175            mDiskNumber(0),
176            mDiskWithCentralDir(0),
177            mNumEntries(0),
178            mTotalNumEntries(0),
179            mCentralDirSize(0),
180            mCentralDirOffset(0),
181            mCommentLen(0),
182            mComment(NULL)
183            {}
184        virtual ~EndOfCentralDir(void) {
185            delete[] mComment;
186        }
187
188        status_t readBuf(const unsigned char* buf, int len);
189        status_t write(FILE* fp);
190
191        //unsigned long   mSignature;
192        unsigned short  mDiskNumber;
193        unsigned short  mDiskWithCentralDir;
194        unsigned short  mNumEntries;
195        unsigned short  mTotalNumEntries;
196        unsigned long   mCentralDirSize;
197        unsigned long   mCentralDirOffset;      // offset from first disk
198        unsigned short  mCommentLen;
199        unsigned char*  mComment;
200
201        enum {
202            kSignature      = 0x06054b50,
203            kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment
204
205            kMaxCommentLen  = 65535,    // longest possible in ushort
206            kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
207
208        };
209
210        void dump(void) const;
211    };
212
213
214    /* read all entries in the central dir */
215    status_t readCentralDir(void);
216
217    /* crunch deleted entries out */
218    status_t crunchArchive(void);
219
220    /* clean up mEntries */
221    void discardEntries(void);
222
223    /* common handler for all "add" functions */
224    status_t addCommon(const char* fileName, const void* data, size_t size,
225        const char* storageName, int sourceType, int compressionMethod,
226        ZipEntry** ppEntry);
227
228    /* copy all of "srcFp" into "dstFp" */
229    status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
230    /* copy all of "data" into "dstFp" */
231    status_t copyDataToFp(FILE* dstFp,
232        const void* data, size_t size, unsigned long* pCRC32);
233    /* copy some of "srcFp" into "dstFp" */
234    status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
235        unsigned long* pCRC32);
236    /* like memmove(), but on parts of a single file */
237    status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
238    /* compress all of "srcFp" into "dstFp", using Deflate */
239    status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
240        const void* data, size_t size, unsigned long* pCRC32);
241
242    /* get modification date from a file descriptor */
243    time_t getModTime(int fd);
244
245    /*
246     * We use stdio FILE*, which gives us buffering but makes dealing
247     * with files >2GB awkward.  Until we support Zip64, we're fine.
248     */
249    FILE*           mZipFp;             // Zip file pointer
250
251    /* one of these per file */
252    EndOfCentralDir mEOCD;
253
254    /* did we open this read-only? */
255    bool            mReadOnly;
256
257    /* set this when we trash the central dir */
258    bool            mNeedCDRewrite;
259
260    /*
261     * One ZipEntry per entry in the zip file.  I'm using pointers instead
262     * of objects because it's easier than making operator= work for the
263     * classes and sub-classes.
264     */
265    Vector<ZipEntry*>   mEntries;
266};
267
268}; // namespace android
269
270#endif // __LIBS_ZIPFILE_H
271