Home | History | Annotate | Download | only in zipalign
      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:
     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,
     81         ZipEntry** ppEntry)
     82     {
     83         return add(fileName, fileName, compressionMethod, ppEntry);
     84     }
     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      */
     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      */
    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      */
    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:
    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             {}
    193         virtual ~EndOfCentralDir(void) {
    194             delete[] mComment;
    195         }
    196 
    197         status_t readBuf(const unsigned char* buf, int len);
    198         status_t write(FILE* fp);
    199 
    200         //unsigned long   mSignature;
    201         unsigned short  mDiskNumber;
    202         unsigned short  mDiskWithCentralDir;
    203         unsigned short  mNumEntries;
    204         unsigned short  mTotalNumEntries;
    205         unsigned long   mCentralDirSize;
    206         unsigned long   mCentralDirOffset;      // offset from first disk
    207         unsigned short  mCommentLen;
    208         unsigned char*  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, unsigned long* pCRC32);
    239     /* copy all of "data" into "dstFp" */
    240     status_t copyDataToFp(FILE* dstFp,
    241         const void* data, size_t size, unsigned long* pCRC32);
    242     /* copy some of "srcFp" into "dstFp" */
    243     status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
    244         unsigned long* 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, unsigned long* 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