Home | History | Annotate | Download | only in google
      1 // Copyright (c) 2012 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.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/files/file.h"
     12 #include "base/files/file_enumerator.h"
     13 #include "base/logging.h"
     14 #include "base/strings/string16.h"
     15 #include "base/strings/string_util.h"
     16 #include "third_party/zlib/google/zip_internal.h"
     17 #include "third_party/zlib/google/zip_reader.h"
     18 
     19 #if defined(USE_SYSTEM_MINIZIP)
     20 #include <minizip/unzip.h>
     21 #include <minizip/zip.h>
     22 #else
     23 #include "third_party/zlib/contrib/minizip/unzip.h"
     24 #include "third_party/zlib/contrib/minizip/zip.h"
     25 #endif
     26 
     27 namespace {
     28 
     29 bool AddFileToZip(zipFile zip_file, const base::FilePath& src_dir) {
     30   base::File file(src_dir, base::File::FLAG_OPEN | base::File::FLAG_READ);
     31   if (!file.IsValid()) {
     32     DLOG(ERROR) << "Could not open file for path " << src_dir.value();
     33     return false;
     34   }
     35 
     36   int num_bytes;
     37   char buf[zip::internal::kZipBufSize];
     38   do {
     39     num_bytes = file.ReadAtCurrentPos(buf, zip::internal::kZipBufSize);
     40     if (num_bytes > 0) {
     41       if (ZIP_OK != zipWriteInFileInZip(zip_file, buf, num_bytes)) {
     42         DLOG(ERROR) << "Could not write data to zip for path "
     43                     << src_dir.value();
     44         return false;
     45       }
     46     }
     47   } while (num_bytes > 0);
     48 
     49   return true;
     50 }
     51 
     52 bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
     53                    const base::FilePath& root_path) {
     54   base::FilePath relative_path;
     55   bool result = root_path.AppendRelativePath(path, &relative_path);
     56   DCHECK(result);
     57   std::string str_path = relative_path.AsUTF8Unsafe();
     58 #if defined(OS_WIN)
     59   ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
     60 #endif
     61 
     62   bool is_directory = base::DirectoryExists(path);
     63   if (is_directory)
     64     str_path += "/";
     65 
     66   zip_fileinfo file_info = zip::internal::GetFileInfoForZipping(path);
     67   if (!zip::internal::ZipOpenNewFileInZip(zip_file, str_path, &file_info))
     68     return false;
     69 
     70   bool success = true;
     71   if (!is_directory) {
     72     success = AddFileToZip(zip_file, path);
     73   }
     74 
     75   if (ZIP_OK != zipCloseFileInZip(zip_file)) {
     76     DLOG(ERROR) << "Could not close zip file entry " << str_path;
     77     return false;
     78   }
     79 
     80   return success;
     81 }
     82 
     83 bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
     84   return true;
     85 }
     86 
     87 bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
     88   return file_path.BaseName().value()[0] != '.';
     89 }
     90 
     91 }  // namespace
     92 
     93 namespace zip {
     94 
     95 bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) {
     96   ZipReader reader;
     97   if (!reader.Open(src_file)) {
     98     DLOG(WARNING) << "Failed to open " << src_file.value();
     99     return false;
    100   }
    101   while (reader.HasMore()) {
    102     if (!reader.OpenCurrentEntryInZip()) {
    103       DLOG(WARNING) << "Failed to open the current file in zip";
    104       return false;
    105     }
    106     if (reader.current_entry_info()->is_unsafe()) {
    107       DLOG(WARNING) << "Found an unsafe file in zip "
    108                     << reader.current_entry_info()->file_path().value();
    109       return false;
    110     }
    111     if (!reader.ExtractCurrentEntryIntoDirectory(dest_dir)) {
    112       DLOG(WARNING) << "Failed to extract "
    113                     << reader.current_entry_info()->file_path().value();
    114       return false;
    115     }
    116     if (!reader.AdvanceToNextEntry()) {
    117       DLOG(WARNING) << "Failed to advance to the next file";
    118       return false;
    119     }
    120   }
    121   return true;
    122 }
    123 
    124 bool ZipWithFilterCallback(const base::FilePath& src_dir,
    125                            const base::FilePath& dest_file,
    126                            const FilterCallback& filter_cb) {
    127   DCHECK(base::DirectoryExists(src_dir));
    128 
    129   zipFile zip_file = internal::OpenForZipping(dest_file.AsUTF8Unsafe(),
    130                                               APPEND_STATUS_CREATE);
    131 
    132   if (!zip_file) {
    133     DLOG(WARNING) << "couldn't create file " << dest_file.value();
    134     return false;
    135   }
    136 
    137   bool success = true;
    138   base::FileEnumerator file_enumerator(src_dir, true /* recursive */,
    139       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
    140   for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
    141        path = file_enumerator.Next()) {
    142     if (!filter_cb.Run(path)) {
    143       continue;
    144     }
    145 
    146     if (!AddEntryToZip(zip_file, path, src_dir)) {
    147       success = false;
    148       break;
    149     }
    150   }
    151 
    152   if (ZIP_OK != zipClose(zip_file, NULL)) {
    153     DLOG(ERROR) << "Error closing zip file " << dest_file.value();
    154     return false;
    155   }
    156 
    157   return success;
    158 }
    159 
    160 bool Zip(const base::FilePath& src_dir, const base::FilePath& dest_file,
    161          bool include_hidden_files) {
    162   if (include_hidden_files) {
    163     return ZipWithFilterCallback(
    164         src_dir, dest_file, base::Bind(&ExcludeNoFilesFilter));
    165   } else {
    166     return ZipWithFilterCallback(
    167         src_dir, dest_file, base::Bind(&ExcludeHiddenFilesFilter));
    168   }
    169 }
    170 
    171 #if defined(OS_POSIX)
    172 bool ZipFiles(const base::FilePath& src_dir,
    173               const std::vector<base::FilePath>& src_relative_paths,
    174               int dest_fd) {
    175   DCHECK(base::DirectoryExists(src_dir));
    176   zipFile zip_file = internal::OpenFdForZipping(dest_fd, APPEND_STATUS_CREATE);
    177 
    178   if (!zip_file) {
    179     DLOG(ERROR) << "couldn't create file for fd " << dest_fd;
    180     return false;
    181   }
    182 
    183   bool success = true;
    184   for (std::vector<base::FilePath>::const_iterator iter =
    185            src_relative_paths.begin();
    186       iter != src_relative_paths.end(); ++iter) {
    187     const base::FilePath& path = src_dir.Append(*iter);
    188     if (!AddEntryToZip(zip_file, path, src_dir)) {
    189       // TODO(hshi): clean up the partial zip file when error occurs.
    190       success = false;
    191       break;
    192     }
    193   }
    194 
    195   if (ZIP_OK != zipClose(zip_file, NULL)) {
    196     DLOG(ERROR) << "Error closing zip file for fd " << dest_fd;
    197     success = false;
    198   }
    199 
    200   return success;
    201 }
    202 #endif  // defined(OS_POSIX)
    203 
    204 }  // namespace zip
    205