15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "third_party/zlib/google/zip.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <string>
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <vector>
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
11effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/files/file.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
145e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string16.h"
155e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "third_party/zlib/google/zip_internal.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "third_party/zlib/google/zip_reader.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(USE_SYSTEM_MINIZIP)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <minizip/unzip.h>
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <minizip/zip.h>
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/zlib/contrib/minizip/unzip.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/zlib/contrib/minizip/zip.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AddFileToZip(zipFile zip_file, const base::FilePath& src_dir) {
30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  base::File file(src_dir, base::File::FLAG_OPEN | base::File::FLAG_READ);
31effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!file.IsValid()) {
32effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    DLOG(ERROR) << "Could not open file for path " << src_dir.value();
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_bytes;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char buf[zip::internal::kZipBufSize];
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  do {
39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    num_bytes = file.ReadAtCurrentPos(buf, zip::internal::kZipBufSize);
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (num_bytes > 0) {
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (ZIP_OK != zipWriteInFileInZip(zip_file, buf, num_bytes)) {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DLOG(ERROR) << "Could not write data to zip for path "
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    << src_dir.value();
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } while (num_bytes > 0);
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AddEntryToZip(zipFile zip_file, const base::FilePath& path,
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   const base::FilePath& root_path) {
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::FilePath relative_path;
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool result = root_path.AppendRelativePath(path, &relative_path);
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DCHECK(result);
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string str_path = relative_path.AsUTF8Unsafe();
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  bool is_directory = base::DirectoryExists(path);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_directory)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    str_path += "/";
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
66010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  zip_fileinfo file_info = zip::internal::GetFileInfoForZipping(path);
67010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!zip::internal::ZipOpenNewFileInZip(zip_file, str_path, &file_info))
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool success = true;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!is_directory) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    success = AddFileToZip(zip_file, path);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ZIP_OK != zipCloseFileInZip(zip_file)) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(ERROR) << "Could not close zip file entry " << str_path;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return success;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ExcludeNoFilesFilter(const base::FilePath& file_path) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return file_path.BaseName().value()[0] != '.';
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace zip {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ZipReader reader;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!reader.Open(src_file)) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(WARNING) << "Failed to open " << src_file.value();
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (reader.HasMore()) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!reader.OpenCurrentEntryInZip()) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(WARNING) << "Failed to open the current file in zip";
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (reader.current_entry_info()->is_unsafe()) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(WARNING) << "Found an unsafe file in zip "
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    << reader.current_entry_info()->file_path().value();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!reader.ExtractCurrentEntryIntoDirectory(dest_dir)) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(WARNING) << "Failed to extract "
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    << reader.current_entry_info()->file_path().value();
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!reader.AdvanceToNextEntry()) {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(WARNING) << "Failed to advance to the next file";
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ZipWithFilterCallback(const base::FilePath& src_dir,
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           const base::FilePath& dest_file,
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           const FilterCallback& filter_cb) {
1277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  DCHECK(base::DirectoryExists(src_dir));
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  zipFile zip_file = internal::OpenForZipping(dest_file.AsUTF8Unsafe(),
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              APPEND_STATUS_CREATE);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!zip_file) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(WARNING) << "couldn't create file " << dest_file.value();
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool success = true;
138868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  base::FileEnumerator file_enumerator(src_dir, true /* recursive */,
139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       path = file_enumerator.Next()) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!filter_cb.Run(path)) {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!AddEntryToZip(zip_file, path, src_dir)) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      success = false;
148010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      break;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ZIP_OK != zipClose(zip_file, NULL)) {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(ERROR) << "Error closing zip file " << dest_file.value();
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return success;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool Zip(const base::FilePath& src_dir, const base::FilePath& dest_file,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         bool include_hidden_files) {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (include_hidden_files) {
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ZipWithFilterCallback(
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        src_dir, dest_file, base::Bind(&ExcludeNoFilesFilter));
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ZipWithFilterCallback(
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        src_dir, dest_file, base::Bind(&ExcludeHiddenFilesFilter));
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_POSIX)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ZipFiles(const base::FilePath& src_dir,
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              const std::vector<base::FilePath>& src_relative_paths,
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              int dest_fd) {
1757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  DCHECK(base::DirectoryExists(src_dir));
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  zipFile zip_file = internal::OpenFdForZipping(dest_fd, APPEND_STATUS_CREATE);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!zip_file) {
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DLOG(ERROR) << "couldn't create file for fd " << dest_fd;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool success = true;
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::vector<base::FilePath>::const_iterator iter =
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           src_relative_paths.begin();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      iter != src_relative_paths.end(); ++iter) {
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& path = src_dir.Append(*iter);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!AddEntryToZip(zip_file, path, src_dir)) {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(hshi): clean up the partial zip file when error occurs.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      success = false;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ZIP_OK != zipClose(zip_file, NULL)) {
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DLOG(ERROR) << "Error closing zip file for fd " << dest_fd;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    success = false;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return success;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif  // defined(OS_POSIX)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace zip
205