Home | History | Annotate | Download | only in google
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "third_party/zlib/google/zip_internal.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/files/file_util.h"
     10 #include "base/logging.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "base/time/time.h"
     13 
     14 #if defined(USE_SYSTEM_MINIZIP)
     15 #include <minizip/ioapi.h>
     16 #include <minizip/unzip.h>
     17 #include <minizip/zip.h>
     18 #else
     19 #include "third_party/zlib/contrib/minizip/unzip.h"
     20 #include "third_party/zlib/contrib/minizip/zip.h"
     21 #if defined(OS_WIN)
     22 #include "third_party/zlib/contrib/minizip/iowin32.h"
     23 #elif defined(OS_POSIX)
     24 #include "third_party/zlib/contrib/minizip/ioapi.h"
     25 #endif  // defined(OS_POSIX)
     26 #endif  // defined(USE_SYSTEM_MINIZIP)
     27 
     28 namespace {
     29 
     30 #if defined(OS_WIN)
     31 typedef struct {
     32   HANDLE hf;
     33   int error;
     34 } WIN32FILE_IOWIN;
     35 
     36 // This function is derived from third_party/minizip/iowin32.c.
     37 // Its only difference is that it treats the char* as UTF8 and
     38 // uses the Unicode version of CreateFile.
     39 void* ZipOpenFunc(void *opaque, const char* filename, int mode) {
     40   DWORD desired_access = 0, creation_disposition = 0;
     41   DWORD share_mode = 0, flags_and_attributes = 0;
     42   HANDLE file = 0;
     43   void* ret = NULL;
     44 
     45   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
     46     desired_access = GENERIC_READ;
     47     creation_disposition = OPEN_EXISTING;
     48     share_mode = FILE_SHARE_READ;
     49   } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
     50     desired_access = GENERIC_WRITE | GENERIC_READ;
     51     creation_disposition = OPEN_EXISTING;
     52   } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
     53     desired_access = GENERIC_WRITE | GENERIC_READ;
     54     creation_disposition = CREATE_ALWAYS;
     55   }
     56 
     57   base::string16 filename16 = base::UTF8ToUTF16(filename);
     58   if ((filename != NULL) && (desired_access != 0)) {
     59     file = CreateFile(filename16.c_str(), desired_access, share_mode,
     60         NULL, creation_disposition, flags_and_attributes, NULL);
     61   }
     62 
     63   if (file == INVALID_HANDLE_VALUE)
     64     file = NULL;
     65 
     66   if (file != NULL) {
     67     WIN32FILE_IOWIN file_ret;
     68     file_ret.hf = file;
     69     file_ret.error = 0;
     70     ret = malloc(sizeof(WIN32FILE_IOWIN));
     71     if (ret == NULL)
     72       CloseHandle(file);
     73     else
     74       *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
     75   }
     76   return ret;
     77 }
     78 #endif
     79 
     80 #if defined(OS_POSIX)
     81 // Callback function for zlib that opens a file stream from a file descriptor.
     82 void* FdOpenFileFunc(void* opaque, const char* filename, int mode) {
     83   FILE* file = NULL;
     84   const char* mode_fopen = NULL;
     85 
     86   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
     87     mode_fopen = "rb";
     88   else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
     89     mode_fopen = "r+b";
     90   else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
     91     mode_fopen = "wb";
     92 
     93   if ((filename != NULL) && (mode_fopen != NULL))
     94     file = fdopen(*static_cast<int*>(opaque), mode_fopen);
     95 
     96   return file;
     97 }
     98 
     99 // We don't actually close the file stream since that would close
    100 // the underlying file descriptor, and we don't own it. However we do need to
    101 // flush buffers and free |opaque| since we malloc'ed it in FillFdOpenFileFunc.
    102 int CloseFileFunc(void* opaque, void* stream) {
    103   fflush(static_cast<FILE*>(stream));
    104   free(opaque);
    105   return 0;
    106 }
    107 
    108 // Fills |pzlib_filecunc_def| appropriately to handle the zip file
    109 // referred to by |fd|.
    110 void FillFdOpenFileFunc(zlib_filefunc_def* pzlib_filefunc_def, int fd) {
    111   fill_fopen_filefunc(pzlib_filefunc_def);
    112   pzlib_filefunc_def->zopen_file = FdOpenFileFunc;
    113   pzlib_filefunc_def->zclose_file = CloseFileFunc;
    114   int* ptr_fd = static_cast<int*>(malloc(sizeof(fd)));
    115   *ptr_fd = fd;
    116   pzlib_filefunc_def->opaque = ptr_fd;
    117 }
    118 #endif  // defined(OS_POSIX)
    119 
    120 #if defined(OS_WIN)
    121 // Callback function for zlib that opens a file stream from a Windows handle.
    122 void* HandleOpenFileFunc(void* opaque, const char* filename, int mode) {
    123   WIN32FILE_IOWIN file_ret;
    124   file_ret.hf = static_cast<HANDLE>(opaque);
    125   file_ret.error = 0;
    126   if (file_ret.hf == INVALID_HANDLE_VALUE)
    127     return NULL;
    128 
    129   void* ret = malloc(sizeof(WIN32FILE_IOWIN));
    130   if (ret != NULL)
    131     *(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
    132   return ret;
    133 }
    134 #endif
    135 
    136 // A struct that contains data required for zlib functions to extract files from
    137 // a zip archive stored in memory directly. The following I/O API functions
    138 // expect their opaque parameters refer to this struct.
    139 struct ZipBuffer {
    140   const char* data;  // weak
    141   size_t length;
    142   size_t offset;
    143 };
    144 
    145 // Opens the specified file. When this function returns a non-NULL pointer, zlib
    146 // uses this pointer as a stream parameter while compressing or uncompressing
    147 // data. (Returning NULL represents an error.) This function initializes the
    148 // given opaque parameter and returns it because this parameter stores all
    149 // information needed for uncompressing data. (This function does not support
    150 // writing compressed data and it returns NULL for this case.)
    151 void* OpenZipBuffer(void* opaque, const char* /*filename*/, int mode) {
    152   if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) {
    153     NOTREACHED();
    154     return NULL;
    155   }
    156   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
    157   if (!buffer || !buffer->data || !buffer->length)
    158     return NULL;
    159   buffer->offset = 0;
    160   return opaque;
    161 }
    162 
    163 // Reads compressed data from the specified stream. This function copies data
    164 // refered by the opaque parameter and returns the size actually copied.
    165 uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) {
    166   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
    167   DCHECK_LE(buffer->offset, buffer->length);
    168   size_t remaining_bytes = buffer->length - buffer->offset;
    169   if (!buffer || !buffer->data || !remaining_bytes)
    170     return 0;
    171   size = std::min(size, static_cast<uLong>(remaining_bytes));
    172   memcpy(buf, &buffer->data[buffer->offset], size);
    173   buffer->offset += size;
    174   return size;
    175 }
    176 
    177 // Writes compressed data to the stream. This function always returns zero
    178 // because this implementation is only for reading compressed data.
    179 uLong WriteZipBuffer(void* /*opaque*/,
    180                      void* /*stream*/,
    181                      const void* /*buf*/,
    182                      uLong /*size*/) {
    183   NOTREACHED();
    184   return 0;
    185 }
    186 
    187 // Returns the offset from the beginning of the data.
    188 long GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) {
    189   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
    190   if (!buffer)
    191     return -1;
    192   return static_cast<long>(buffer->offset);
    193 }
    194 
    195 // Moves the current offset to the specified position.
    196 long SeekZipBuffer(void* opaque, void* /*stream*/, uLong offset, int origin) {
    197   ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
    198   if (!buffer)
    199     return -1;
    200   if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
    201     buffer->offset = std::min(buffer->offset + static_cast<size_t>(offset),
    202                               buffer->length);
    203     return 0;
    204   }
    205   if (origin == ZLIB_FILEFUNC_SEEK_END) {
    206     buffer->offset = (buffer->length > offset) ? buffer->length - offset : 0;
    207     return 0;
    208   }
    209   if (origin == ZLIB_FILEFUNC_SEEK_SET) {
    210     buffer->offset = std::min(buffer->length, static_cast<size_t>(offset));
    211     return 0;
    212   }
    213   NOTREACHED();
    214   return -1;
    215 }
    216 
    217 // Closes the input offset and deletes all resources used for compressing or
    218 // uncompressing data. This function deletes the ZipBuffer object referred by
    219 // the opaque parameter since zlib deletes the unzFile object and it does not
    220 // use this object any longer.
    221 int CloseZipBuffer(void* opaque, void* /*stream*/) {
    222   if (opaque)
    223     free(opaque);
    224   return 0;
    225 }
    226 
    227 // Returns the last error happened when reading or writing data. This function
    228 // always returns zero, which means there are not any errors.
    229 int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) {
    230   return 0;
    231 }
    232 
    233 // Returns a zip_fileinfo struct with the time represented by |file_time|.
    234 zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) {
    235   base::Time::Exploded file_time_parts;
    236   file_time.LocalExplode(&file_time_parts);
    237 
    238   zip_fileinfo zip_info = {};
    239   if (file_time_parts.year >= 1980) {
    240     // This if check works around the handling of the year value in
    241     // contrib/minizip/zip.c in function zip64local_TmzDateToDosDate
    242     // It assumes that dates below 1980 are in the double digit format.
    243     // Hence the fail safe option is to leave the date unset. Some programs
    244     // might show the unset date as 1980-0-0 which is invalid.
    245     zip_info.tmz_date.tm_year = file_time_parts.year;
    246     zip_info.tmz_date.tm_mon = file_time_parts.month - 1;
    247     zip_info.tmz_date.tm_mday = file_time_parts.day_of_month;
    248     zip_info.tmz_date.tm_hour = file_time_parts.hour;
    249     zip_info.tmz_date.tm_min = file_time_parts.minute;
    250     zip_info.tmz_date.tm_sec = file_time_parts.second;
    251   }
    252 
    253   return zip_info;
    254 }
    255 }  // namespace
    256 
    257 namespace zip {
    258 namespace internal {
    259 
    260 unzFile OpenForUnzipping(const std::string& file_name_utf8) {
    261   zlib_filefunc_def* zip_func_ptrs = NULL;
    262 #if defined(OS_WIN)
    263   zlib_filefunc_def zip_funcs;
    264   fill_win32_filefunc(&zip_funcs);
    265   zip_funcs.zopen_file = ZipOpenFunc;
    266   zip_func_ptrs = &zip_funcs;
    267 #endif
    268   return unzOpen2(file_name_utf8.c_str(), zip_func_ptrs);
    269 }
    270 
    271 #if defined(OS_POSIX)
    272 unzFile OpenFdForUnzipping(int zip_fd) {
    273   zlib_filefunc_def zip_funcs;
    274   FillFdOpenFileFunc(&zip_funcs, zip_fd);
    275   // Passing dummy "fd" filename to zlib.
    276   return unzOpen2("fd", &zip_funcs);
    277 }
    278 #endif
    279 
    280 #if defined(OS_WIN)
    281 unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
    282   zlib_filefunc_def zip_funcs;
    283   fill_win32_filefunc(&zip_funcs);
    284   zip_funcs.zopen_file = HandleOpenFileFunc;
    285   zip_funcs.opaque = zip_handle;
    286   return unzOpen2("fd", &zip_funcs);
    287 }
    288 #endif
    289 
    290 // static
    291 unzFile PrepareMemoryForUnzipping(const std::string& data) {
    292   if (data.empty())
    293     return NULL;
    294 
    295   ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer)));
    296   if (!buffer)
    297     return NULL;
    298   buffer->data = data.data();
    299   buffer->length = data.length();
    300   buffer->offset = 0;
    301 
    302   zlib_filefunc_def zip_functions;
    303   zip_functions.zopen_file = OpenZipBuffer;
    304   zip_functions.zread_file = ReadZipBuffer;
    305   zip_functions.zwrite_file = WriteZipBuffer;
    306   zip_functions.ztell_file = GetOffsetOfZipBuffer;
    307   zip_functions.zseek_file = SeekZipBuffer;
    308   zip_functions.zclose_file = CloseZipBuffer;
    309   zip_functions.zerror_file = GetErrorOfZipBuffer;
    310   zip_functions.opaque = static_cast<void*>(buffer);
    311   return unzOpen2(NULL, &zip_functions);
    312 }
    313 
    314 zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) {
    315   zlib_filefunc_def* zip_func_ptrs = NULL;
    316 #if defined(OS_WIN)
    317   zlib_filefunc_def zip_funcs;
    318   fill_win32_filefunc(&zip_funcs);
    319   zip_funcs.zopen_file = ZipOpenFunc;
    320   zip_func_ptrs = &zip_funcs;
    321 #endif
    322   return zipOpen2(file_name_utf8.c_str(),
    323                   append_flag,
    324                   NULL,  // global comment
    325                   zip_func_ptrs);
    326 }
    327 
    328 #if defined(OS_POSIX)
    329 zipFile OpenFdForZipping(int zip_fd, int append_flag) {
    330   zlib_filefunc_def zip_funcs;
    331   FillFdOpenFileFunc(&zip_funcs, zip_fd);
    332   // Passing dummy "fd" filename to zlib.
    333   return zipOpen2("fd", append_flag, NULL, &zip_funcs);
    334 }
    335 #endif
    336 
    337 zip_fileinfo GetFileInfoForZipping(const base::FilePath& path) {
    338   base::Time file_time;
    339   base::File::Info file_info;
    340   if (base::GetFileInfo(path, &file_info))
    341     file_time = file_info.last_modified;
    342   return TimeToZipFileInfo(file_time);
    343 }
    344 
    345 bool ZipOpenNewFileInZip(zipFile zip_file,
    346                          const std::string& str_path,
    347                          const zip_fileinfo* file_info) {
    348   // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
    349   // Setting the Language encoding flag so the file is told to be in utf-8.
    350   const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
    351 
    352   if (ZIP_OK != zipOpenNewFileInZip4(
    353                     zip_file,  // file
    354                     str_path.c_str(),  // filename
    355                     file_info,  // zipfi
    356                     NULL,  // extrafield_local,
    357                     0u,  // size_extrafield_local
    358                     NULL,  // extrafield_global
    359                     0u,  // size_extrafield_global
    360                     NULL,  // comment
    361                     Z_DEFLATED,  // method
    362                     Z_DEFAULT_COMPRESSION,  // level
    363                     0,  // raw
    364                     -MAX_WBITS,  // windowBits
    365                     DEF_MEM_LEVEL,  // memLevel
    366                     Z_DEFAULT_STRATEGY,  // strategy
    367                     NULL,  // password
    368                     0,  // crcForCrypting
    369                     0,  // versionMadeBy
    370                     LANGUAGE_ENCODING_FLAG)) {  // flagBase
    371     DLOG(ERROR) << "Could not open zip file entry " << str_path;
    372     return false;
    373   }
    374   return true;
    375 }
    376 
    377 }  // namespace internal
    378 }  // namespace zip
    379