Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

59
deps/zlib/google/BUILD.gn vendored Normal file
View File

@ -0,0 +1,59 @@
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build_overrides/build.gni")
if (build_with_chromium && is_ios) {
import("//build/config/ios/bundle_data_from_filelist.gni")
}
if (build_with_chromium) {
static_library("zip") {
sources = [
"redact.h",
"zip.cc",
"zip.h",
"zip_internal.cc",
"zip_internal.h",
"zip_reader.cc",
"zip_reader.h",
"zip_writer.cc",
"zip_writer.h",
]
deps = [
"..:minizip",
"//base",
"//base:i18n",
]
}
static_library("compression_utils") {
sources = [
"compression_utils.cc",
"compression_utils.h",
]
deps = [
"..",
"//base",
]
public_deps = [ ":compression_utils_portable" ]
}
if (is_ios) {
bundle_data_from_filelist("zlib_pak_bundle_data") {
testonly = true
filelist_name = "test_data.filelist"
}
}
}
# This allows other users of Chromium's zlib library, but don't use Chromium's
# //base, to reuse some boilerplate code.
static_library("compression_utils_portable") {
sources = [
"compression_utils_portable.cc",
"compression_utils_portable.h",
]
public_deps = [ ".." ]
}

7
deps/zlib/google/DEPS vendored Normal file
View File

@ -0,0 +1,7 @@
include_rules = [
'+base',
'+build',
'+testing',
'+third_party/icu/source/i18n/unicode',
'+third_party/zlib/zlib.h',
]

6
deps/zlib/google/OWNERS vendored Normal file
View File

@ -0,0 +1,6 @@
satorux@chromium.org
# compression_utils*
asvitkine@chromium.org
isherman@chromium.org
cavalcantii@chromium.org

132
deps/zlib/google/compression_utils.cc vendored Normal file
View File

@ -0,0 +1,132 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/compression_utils.h"
#include "base/check_op.h"
#include "base/process/memory.h"
#include "third_party/zlib/google/compression_utils_portable.h"
namespace compression {
bool GzipCompress(base::span<const char> input,
char* output_buffer,
size_t output_buffer_size,
size_t* compressed_size,
void* (*malloc_fn)(size_t),
void (*free_fn)(void*)) {
static_assert(sizeof(Bytef) == 1, "");
// uLongf can be larger than size_t.
uLongf compressed_size_long = static_cast<uLongf>(output_buffer_size);
if (zlib_internal::GzipCompressHelper(
reinterpret_cast<Bytef*>(output_buffer), &compressed_size_long,
reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size()), malloc_fn, free_fn) != Z_OK) {
return false;
}
// No overflow, as compressed_size_long <= output.size() which is a size_t.
*compressed_size = static_cast<size_t>(compressed_size_long);
return true;
}
bool GzipCompress(base::span<const char> input, std::string* output) {
return GzipCompress(base::as_bytes(input), output);
}
bool GzipCompress(base::span<const uint8_t> input, std::string* output) {
// Not using std::vector<> because allocation failures are recoverable,
// which is hidden by std::vector<>.
static_assert(sizeof(Bytef) == 1, "");
const uLongf input_size = static_cast<uLongf>(input.size());
uLongf compressed_data_size =
zlib_internal::GzipExpectedCompressedSize(input_size);
Bytef* compressed_data;
if (!base::UncheckedMalloc(compressed_data_size,
reinterpret_cast<void**>(&compressed_data))) {
return false;
}
if (zlib_internal::GzipCompressHelper(
compressed_data, &compressed_data_size,
reinterpret_cast<const Bytef*>(input.data()), input_size, nullptr,
nullptr) != Z_OK) {
free(compressed_data);
return false;
}
Bytef* resized_data =
reinterpret_cast<Bytef*>(realloc(compressed_data, compressed_data_size));
if (!resized_data) {
free(compressed_data);
return false;
}
output->assign(resized_data, resized_data + compressed_data_size);
DCHECK_EQ(input_size, GetUncompressedSize(*output));
free(resized_data);
return true;
}
bool GzipUncompress(const std::string& input, std::string* output) {
std::string uncompressed_output;
uLongf uncompressed_size = static_cast<uLongf>(GetUncompressedSize(input));
if (size_t{uncompressed_size} > uncompressed_output.max_size())
return false;
uncompressed_output.resize(uncompressed_size);
if (zlib_internal::GzipUncompressHelper(
reinterpret_cast<Bytef*>(uncompressed_output.data()),
&uncompressed_size, reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.length())) == Z_OK) {
output->swap(uncompressed_output);
return true;
}
return false;
}
bool GzipUncompress(base::span<const char> input, base::span<char> output) {
return GzipUncompress(base::as_bytes(input), base::as_writable_bytes(output));
}
bool GzipUncompress(base::span<const uint8_t> input,
base::span<uint8_t> output) {
uLongf uncompressed_size = GetUncompressedSize(input);
if (uncompressed_size > output.size())
return false;
return zlib_internal::GzipUncompressHelper(
reinterpret_cast<Bytef*>(output.data()), &uncompressed_size,
reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
bool GzipUncompress(base::span<const char> input, std::string* output) {
return GzipUncompress(base::as_bytes(input), output);
}
bool GzipUncompress(base::span<const uint8_t> input, std::string* output) {
// Disallow in-place usage, i.e., |input| using |*output| as underlying data.
DCHECK_NE(reinterpret_cast<const char*>(input.data()), output->data());
uLongf uncompressed_size = GetUncompressedSize(input);
output->resize(uncompressed_size);
return zlib_internal::GzipUncompressHelper(
reinterpret_cast<Bytef*>(output->data()), &uncompressed_size,
reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
uint32_t GetUncompressedSize(base::span<const char> compressed_data) {
return GetUncompressedSize(base::as_bytes(compressed_data));
}
uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data) {
return zlib_internal::GetGzipUncompressedSize(
reinterpret_cast<const Bytef*>(compressed_data.data()),
compressed_data.size());
}
} // namespace compression

69
deps/zlib/google/compression_utils.h vendored Normal file
View File

@ -0,0 +1,69 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_COMPRESSION_UTILS_H_
#define THIRD_PARTY_ZLIB_GOOGLE_COMPRESSION_UTILS_H_
#include <string>
#include "base/containers/span.h"
namespace compression {
// Compresses the data in |input| using gzip, storing the result in
// |output_buffer|, of size |output_buffer_size|. If the buffer is large enough
// and compression succeeds, |compressed_size| points to the compressed data
// size after the call.
// |malloc_fn| and |free_fn| are pointers to malloc() and free()-like functions,
// or nullptr to use the standard ones.
// Returns true for success.
bool GzipCompress(base::span<const char> input,
char* output_buffer,
size_t output_buffer_size,
size_t* compressed_size,
void* (*malloc_fn)(size_t),
void (*free_fn)(void*));
// Compresses the data in |input| using gzip, storing the result in |output|.
// |input| and |output| are allowed to point to the same string (in-place
// operation).
// Returns true for success.
bool GzipCompress(base::span<const char> input, std::string* output);
// Like the above method, but using uint8_t instead.
bool GzipCompress(base::span<const uint8_t> input, std::string* output);
// Uncompresses the data in |input| using gzip, storing the result in |output|.
// |input| and |output| are allowed to be the same string (in-place operation).
// Returns true for success.
bool GzipUncompress(const std::string& input, std::string* output);
// Like the above method, but uses base::span to avoid allocations if
// needed. |output|'s size must be at least as large as the return value from
// GetUncompressedSize.
// Returns true for success.
bool GzipUncompress(base::span<const char> input, base::span<char> output);
// Like the above method, but using uint8_t instead.
bool GzipUncompress(base::span<const uint8_t> input,
base::span<uint8_t> output);
// Uncompresses the data in |input| using gzip, and writes the results to
// |output|, which must NOT be the underlying string of |input|, and is resized
// if necessary.
// Returns true for success.
bool GzipUncompress(base::span<const char> input, std::string* output);
// Like the above method, but using uint8_t instead.
bool GzipUncompress(base::span<const uint8_t> input, std::string* output);
// Returns the uncompressed size from GZIP-compressed |compressed_data|.
uint32_t GetUncompressedSize(base::span<const char> compressed_data);
// Like the above method, but using uint8_t instead.
uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data);
} // namespace compression
#endif // THIRD_PARTY_ZLIB_GOOGLE_COMPRESSION_UTILS_H_

View File

@ -0,0 +1,205 @@
/* compression_utils_portable.cc
*
* Copyright 2019 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
#include "compression_utils_portable.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
namespace zlib_internal {
// The difference in bytes between a zlib header and a gzip header.
const size_t kGzipZlibHeaderDifferenceBytes = 16;
// Pass an integer greater than the following get a gzip header instead of a
// zlib header when calling deflateInit2() and inflateInit2().
const int kWindowBitsToGetGzipHeader = 16;
// This describes the amount of memory zlib uses to compress data. It can go
// from 1 to 9, with 8 being the default. For details, see:
// http://www.zlib.net/manual.html (search for memLevel).
const int kZlibMemoryLevel = 8;
// The expected compressed size is based on the input size factored by
// internal Zlib constants (e.g. window size, etc) plus the wrapper
// header size.
uLongf GzipExpectedCompressedSize(uLongf input_size) {
return kGzipZlibHeaderDifferenceBytes + compressBound(input_size);
}
// The expected decompressed size is stored in the last
// 4 bytes of |input| in LE. See https://tools.ietf.org/html/rfc1952#page-5
uint32_t GetGzipUncompressedSize(const Bytef* compressed_data, size_t length) {
uint32_t size;
if (length < sizeof(size))
return 0;
memcpy(&size, &compressed_data[length - sizeof(size)], sizeof(size));
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return size;
#else
return __builtin_bswap32(size);
#endif
}
// The number of window bits determines the type of wrapper to use - see
// https://cs.chromium.org/chromium/src/third_party/zlib/zlib.h?l=566
inline int ZlibStreamWrapperType(WrapperType type) {
if (type == ZLIB) // zlib DEFLATE stream wrapper
return MAX_WBITS;
if (type == GZIP) // gzip DEFLATE stream wrapper
return MAX_WBITS + kWindowBitsToGetGzipHeader;
if (type == ZRAW) // no wrapper, use raw DEFLATE
return -MAX_WBITS;
return 0;
}
int GzipCompressHelper(Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length,
void* (*malloc_fn)(size_t),
void (*free_fn)(void*)) {
return CompressHelper(GZIP, dest, dest_length, source, source_length,
Z_DEFAULT_COMPRESSION, malloc_fn, free_fn);
}
// This code is taken almost verbatim from third_party/zlib/compress.c. The only
// difference is deflateInit2() is called which allows different window bits to
// be set. > 16 causes a gzip header to be emitted rather than a zlib header,
// and negative causes no header to emitted.
//
// Compression level can be a number from 1-9, with 1 being the fastest, 9 being
// the best compression. The default, which the GZIP helper uses, is 6.
int CompressHelper(WrapperType wrapper_type,
Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length,
int compression_level,
void* (*malloc_fn)(size_t),
void (*free_fn)(void*)) {
if (compression_level < 0 || compression_level > 9) {
compression_level = Z_DEFAULT_COMPRESSION;
}
z_stream stream;
// FIXME(cavalcantii): z_const is not defined as 'const'.
stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source));
stream.avail_in = static_cast<uInt>(source_length);
stream.next_out = dest;
stream.avail_out = static_cast<uInt>(*dest_length);
if (static_cast<uLong>(stream.avail_out) != *dest_length)
return Z_BUF_ERROR;
// Cannot convert capturing lambdas to function pointers directly, hence the
// structure.
struct MallocFreeFunctions {
void* (*malloc_fn)(size_t);
void (*free_fn)(void*);
} malloc_free = {malloc_fn, free_fn};
if (malloc_fn) {
if (!free_fn)
return Z_BUF_ERROR;
auto zalloc = [](void* opaque, uInt items, uInt size) {
return reinterpret_cast<MallocFreeFunctions*>(opaque)->malloc_fn(items *
size);
};
auto zfree = [](void* opaque, void* address) {
return reinterpret_cast<MallocFreeFunctions*>(opaque)->free_fn(address);
};
stream.zalloc = static_cast<alloc_func>(zalloc);
stream.zfree = static_cast<free_func>(zfree);
stream.opaque = static_cast<voidpf>(&malloc_free);
} else {
stream.zalloc = static_cast<alloc_func>(0);
stream.zfree = static_cast<free_func>(0);
stream.opaque = static_cast<voidpf>(0);
}
int err = deflateInit2(&stream, compression_level, Z_DEFLATED,
ZlibStreamWrapperType(wrapper_type), kZlibMemoryLevel,
Z_DEFAULT_STRATEGY);
if (err != Z_OK)
return err;
// This has to exist outside of the if statement to prevent it going off the
// stack before deflate(), which will use this object.
gz_header gzip_header;
if (wrapper_type == GZIP) {
memset(&gzip_header, 0, sizeof(gzip_header));
err = deflateSetHeader(&stream, &gzip_header);
if (err != Z_OK)
return err;
}
err = deflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
deflateEnd(&stream);
return err == Z_OK ? Z_BUF_ERROR : err;
}
*dest_length = stream.total_out;
err = deflateEnd(&stream);
return err;
}
int GzipUncompressHelper(Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length) {
return UncompressHelper(GZIP, dest, dest_length, source, source_length);
}
// This code is taken almost verbatim from third_party/zlib/uncompr.c. The only
// difference is inflateInit2() is called which allows different window bits to
// be set. > 16 causes a gzip header to be emitted rather than a zlib header,
// and negative causes no header to emitted.
int UncompressHelper(WrapperType wrapper_type,
Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length) {
z_stream stream;
// FIXME(cavalcantii): z_const is not defined as 'const'.
stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source));
stream.avail_in = static_cast<uInt>(source_length);
if (static_cast<uLong>(stream.avail_in) != source_length)
return Z_BUF_ERROR;
stream.next_out = dest;
stream.avail_out = static_cast<uInt>(*dest_length);
if (static_cast<uLong>(stream.avail_out) != *dest_length)
return Z_BUF_ERROR;
stream.zalloc = static_cast<alloc_func>(0);
stream.zfree = static_cast<free_func>(0);
int err = inflateInit2(&stream, ZlibStreamWrapperType(wrapper_type));
if (err != Z_OK)
return err;
err = inflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
inflateEnd(&stream);
if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
return Z_DATA_ERROR;
return err;
}
*dest_length = stream.total_out;
err = inflateEnd(&stream);
return err;
}
} // namespace zlib_internal

View File

@ -0,0 +1,63 @@
/* compression_utils_portable.h
*
* Copyright 2019 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
#ifndef THIRD_PARTY_ZLIB_GOOGLE_COMPRESSION_UTILS_PORTABLE_H_
#define THIRD_PARTY_ZLIB_GOOGLE_COMPRESSION_UTILS_PORTABLE_H_
#include <stdint.h>
/* TODO(cavalcantii): remove support for Chromium ever building with a system
* zlib.
*/
#if defined(USE_SYSTEM_ZLIB)
#include <zlib.h>
/* AOSP build requires relative paths. */
#else
#include "zlib.h"
#endif
namespace zlib_internal {
enum WrapperType {
ZLIB,
GZIP,
ZRAW,
};
uLongf GzipExpectedCompressedSize(uLongf input_size);
uint32_t GetGzipUncompressedSize(const Bytef* compressed_data, size_t length);
int GzipCompressHelper(Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length,
void* (*malloc_fn)(size_t),
void (*free_fn)(void*));
int CompressHelper(WrapperType wrapper_type,
Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length,
int compression_level,
void* (*malloc_fn)(size_t),
void (*free_fn)(void*));
int GzipUncompressHelper(Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length);
int UncompressHelper(WrapperType wrapper_type,
Bytef* dest,
uLongf* dest_length,
const Bytef* source,
uLong source_length);
} // namespace zlib_internal
#endif // THIRD_PARTY_ZLIB_GOOGLE_COMPRESSION_UTILS_PORTABLE_H_

View File

@ -0,0 +1,99 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/compression_utils.h"
#include <stddef.h>
#include <stdint.h>
#include <iterator>
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
namespace compression {
namespace {
// The data to be compressed by gzip. This is the hex representation of "hello
// world".
const uint8_t kData[] = {0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
0x77, 0x6f, 0x72, 0x6c, 0x64};
// This is the string representation of gzip compressed string above. It was
// obtained by running echo -n "hello world" | gzip -c | hexdump -e '8 1 ",
// 0x%x"' followed by 0'ing out the OS byte (10th byte) in the header. This is
// so that the test passes on all platforms (that run various OS'es).
const uint8_t kCompressedData[] = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb,
0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0x01,
0x00, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00};
} // namespace
TEST(CompressionUtilsTest, GzipCompression) {
std::string data(reinterpret_cast<const char*>(kData), std::size(kData));
std::string compressed_data;
EXPECT_TRUE(GzipCompress(data, &compressed_data));
std::string golden_compressed_data(
reinterpret_cast<const char*>(kCompressedData),
std::size(kCompressedData));
EXPECT_EQ(golden_compressed_data, compressed_data);
}
TEST(CompressionUtilsTest, GzipUncompression) {
std::string compressed_data(reinterpret_cast<const char*>(kCompressedData),
std::size(kCompressedData));
std::string uncompressed_data;
EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data));
std::string golden_data(reinterpret_cast<const char*>(kData),
std::size(kData));
EXPECT_EQ(golden_data, uncompressed_data);
}
TEST(CompressionUtilsTest, GzipUncompressionFromSpanToString) {
std::string uncompressed_data;
EXPECT_TRUE(GzipUncompress(kCompressedData, &uncompressed_data));
std::string golden_data(reinterpret_cast<const char*>(kData),
std::size(kData));
EXPECT_EQ(golden_data, uncompressed_data);
}
// Checks that compressing/decompressing input > 256 bytes works as expected.
TEST(CompressionUtilsTest, LargeInput) {
const size_t kSize = 32 * 1024;
// Generate a data string of |kSize| for testing.
std::string data;
data.resize(kSize);
for (size_t i = 0; i < kSize; ++i)
data[i] = static_cast<char>(i & 0xFF);
std::string compressed_data;
EXPECT_TRUE(GzipCompress(data, &compressed_data));
std::string uncompressed_data;
EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data));
EXPECT_EQ(data, uncompressed_data);
}
TEST(CompressionUtilsTest, InPlace) {
const std::string original_data(reinterpret_cast<const char*>(kData),
std::size(kData));
const std::string golden_compressed_data(
reinterpret_cast<const char*>(kCompressedData),
std::size(kCompressedData));
std::string data(original_data);
EXPECT_TRUE(GzipCompress(data, &data));
EXPECT_EQ(golden_compressed_data, data);
EXPECT_TRUE(GzipUncompress(data, &data));
EXPECT_EQ(original_data, data);
}
} // namespace compression

31
deps/zlib/google/redact.h vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_REDACT_H_
#define THIRD_PARTY_ZLIB_GOOGLE_REDACT_H_
#include <ostream>
#include "base/files/file_path.h"
#include "base/logging.h"
namespace zip {
// Redacts file paths in log messages.
// Example:
// LOG(ERROR) << "Cannot open " << Redact(path);
class Redact {
public:
explicit Redact(const base::FilePath& path) : path_(path) {}
friend std::ostream& operator<<(std::ostream& out, const Redact&& r) {
return LOG_IS_ON(INFO) ? out << "'" << r.path_ << "'" : out << "(redacted)";
}
private:
const base::FilePath& path_;
};
} // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_REDACT_H_

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

15
deps/zlib/google/test/data/README.md vendored Normal file
View File

@ -0,0 +1,15 @@
## test\_posix\_permissions.zip
Rebuild this zip by running:
```
rm test_posix_permissions.zip &&
mkdir z &&
cd z &&
touch 0.txt 1.txt 2.txt 3.txt &&
chmod a+x 0.txt &&
chmod o+x 1.txt &&
chmod u+x 2.txt &&
zip test_posix_permissions.zip * &&
mv test_posix_permissions.zip .. &&
cd .. &&
rm -r z
```

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
deps/zlib/google/test/data/Wrong CRC.zip vendored Normal file

Binary file not shown.

17
deps/zlib/google/test/data/create_test_zip.sh vendored Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
#
# Copyright 2011 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Run this script in its directory to recreate test.zip, test_nocompress.zip,
# and test_encrypted.zip
rm test.zip
rm test_nocompress.zip
rm test_encrypted.zip
pushd test
zip -r ../test.zip .
zip -r -0 ../test_nocompress.zip .
zip -P "fake_password" -r ../test_encrypted.zip .
popd

BIN
deps/zlib/google/test/data/empty.zip vendored Normal file

Binary file not shown.

BIN
deps/zlib/google/test/data/evil.zip vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
deps/zlib/google/test/data/test.zip vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
foo

View File

@ -0,0 +1 @@
bar

View File

@ -0,0 +1 @@
hidden

View File

@ -0,0 +1 @@
baz

View File

@ -0,0 +1,39 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sem urna, semper ac, adipiscing sed, varius eget, quam. Fusce ipsum dolor, condimentum et, ornare in, consequat ac, nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla feugiat justo ac enim. Ut vehicula dapibus elit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam lacus sem, viverra in, laoreet quis, cursus non, odio. Maecenas in elit. Pellentesque vitae dui. Ut dictum tellus et orci. Sed consectetur. Fusce tincidunt. Cras vulputate adipiscing metus. Aliquam lacinia. Vestibulum leo. Donec enim. Etiam imperdiet libero. Nam tellus ante, lobortis eget, dapibus vel, accumsan ultricies, nibh. In ultrices mauris at est.
Phasellus ac elit sit amet felis aliquet venenatis. Sed porta libero at massa. Fusce blandit diam eget lorem. Nullam velit diam, fermentum eget, scelerisque non, faucibus a, tellus. Ut accumsan eros vitae velit. Proin vehicula aliquam felis. Proin ligula augue, gravida a, fermentum sit amet, sodales ac, purus. Phasellus sagittis scelerisque tellus. Ut gravida metus quis enim. Curabitur malesuada orci in massa. Pellentesque nibh turpis, adipiscing vel, rutrum quis, rhoncus vel, dolor. Pellentesque suscipit, arcu in egestas semper, lorem sapien vehicula nunc, non scelerisque lectus ante at diam. Etiam varius leo. Sed dictum, ante sed imperdiet ornare, felis leo lobortis eros, eu tincidunt lorem tellus sit amet mi. Mauris nisl elit, placerat non, scelerisque id, congue non, ligula. Nulla eget mi eu dolor scelerisque tempor. Fusce non lorem vel nibh mattis blandit.
Vivamus sit amet urna quis eros varius condimentum. Phasellus placerat ligula ac felis. Vivamus tempor, metus id fermentum feugiat, justo ligula malesuada nisl, ut vulputate nisi arcu in eros. Vestibulum non erat in massa laoreet luctus. Praesent tempor, tortor eget sodales condimentum, purus odio gravida tellus, pellentesque euismod enim diam id augue. Duis malesuada lacus eu felis. Suspendisse tristique faucibus arcu. Etiam ac sapien. Aenean dolor enim, semper auctor, dapibus cursus, varius et, arcu. Aenean pede nunc, varius nec, pulvinar a, accumsan id, dui. Nulla a risus eget nibh pharetra rutrum. Quisque adipiscing adipiscing neque. Praesent faucibus lacus eu nulla. Vestibulum nec nunc vel quam cursus sollicitudin. Nam at quam at neque pharetra elementum. Sed ornare ullamcorper nibh.
Aliquam aliquam vulputate diam. Ut molestie pharetra neque. Pellentesque sodales blandit nibh. Donec eget mauris eget mauris blandit pharetra. Etiam imperdiet commodo tellus. Nulla felis justo, vestibulum hendrerit, posuere vitae, viverra ut, risus. Pellentesque auctor, ante egestas suscipit vestibulum, sem dolor posuere urna, pellentesque dignissim ante orci a lectus. Suspendisse potenti. Sed et pede pretium arcu porttitor porta. Nullam nec nulla. Fusce lacinia, massa ac fermentum congue, dui quam varius mauris, vitae ullamcorper pede lacus at nisl. Suspendisse aliquam pede a sapien. Morbi pellentesque, mi id mattis aliquet, nisl erat tincidunt lorem, eu bibendum nunc nisi nec erat. Suspendisse commodo dapibus dolor. Quisque id risus. Nullam gravida est tempor lectus.
Morbi mattis. Cras sagittis. Sed eleifend odio sit amet nibh. Cras sit amet massa. Donec congue nulla vel augue. Maecenas fermentum turpis at diam. In lobortis. Phasellus aliquet. Etiam eu diam. Integer accumsan. Integer sagittis tempor mi. Aliquam interdum lectus id augue. Donec vel eros. Donec vitae orci a sapien imperdiet consectetur.
Maecenas est lorem, luctus vel, fringilla sed, rutrum nec, nisl. Donec sapien. Vestibulum at metus. Praesent erat. Nulla semper metus a lacus. Morbi ultrices. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus odio. Ut mauris. Fusce et risus. Proin at mauris a eros imperdiet mollis. Sed adipiscing hendrerit nisl. Curabitur libero. Donec nulla. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Nullam non lacus. Vivamus rhoncus dolor sit amet ipsum. Vivamus volutpat eleifend odio. Vestibulum nunc. In at magna a pede gravida vulputate. Praesent ligula tellus, rhoncus elementum, pulvinar a, condimentum vitae, libero. Donec ornare dui. Proin non lorem quis massa placerat interdum. Nam justo. Aliquam rutrum dignissim mi. Mauris tellus. Proin dui felis, lacinia quis, volutpat a, blandit sed, neque. Etiam non dolor eu justo interdum tristique. Morbi volutpat, elit vitae rutrum iaculis, augue mi accumsan metus, sed porttitor felis mi sed felis.
Nulla quam lectus, pharetra non, sodales sit amet, pretium quis, massa. Aliquam orci. Duis id massa. Pellentesque porttitor nibh id quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec convallis, pede vitae volutpat consequat, justo lorem ultricies nisi, ut scelerisque leo lorem in tellus. Cras bibendum. Sed hendrerit placerat tortor. Sed felis. Aliquam erat volutpat. Praesent rhoncus nisi. Vestibulum justo felis, tincidunt luctus, suscipit sed, tincidunt ut, lorem. Proin non risus ac mi mattis volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam pellentesque nisl sit amet sapien rutrum porta.
Phasellus ut pede non odio ultricies egestas. Aenean nibh. Mauris magna felis, posuere non, tempus et, bibendum ut, felis. Donec vel ipsum ac nisi dapibus lobortis. Sed congue ligula at est. Vivamus elit est, rhoncus vitae, tempus at, consectetur nec, elit. Duis vehicula tellus vitae sem. Suspendisse rhoncus felis sit amet magna. Maecenas vulputate risus in diam. Nunc sollicitudin risus eget quam. Nam pretium purus eleifend dolor. Integer erat nulla, mattis eu, sollicitudin sed, sagittis nec, metus.
Donec ante felis, ultrices nec, pellentesque nec, elementum et, nunc. Pellentesque in tortor sit amet urna aliquet tincidunt. Praesent ultrices. Sed tincidunt. Donec turpis enim, feugiat eu, cursus ut, viverra ut, odio. Morbi in orci quis enim vehicula lobortis. Proin in neque. Pellentesque venenatis neque sit amet augue. Proin suscipit nibh. Praesent et diam.
In eget ligula quis libero vulputate eleifend. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc non urna vitae mi vulputate adipiscing. Nulla vitae arcu. Nullam ut nisl ac enim pulvinar auctor. Suspendisse tortor pede, consectetur in, volutpat at, condimentum vel, ligula. Pellentesque nisi est, vulputate vitae, hendrerit id, venenatis sit amet, urna. Nunc lobortis. Sed rhoncus, tortor vitae pellentesque facilisis, massa ante interdum massa, feugiat faucibus pede nunc et tellus. Quisque auctor sollicitudin nibh. Aliquam congue. Praesent ullamcorper mi id lacus. Nullam ac risus et velit ullamcorper vehicula. Vestibulum consectetur convallis neque. Donec dapibus, ante a rutrum interdum, mi dui auctor lacus, quis posuere eros diam eu nisl. Integer nec velit vel purus eleifend condimentum. Cras quis nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Sed euismod, metus eu consectetur dictum, felis eros sodales pede, ac pellentesque lacus orci sed velit. Praesent a justo. Aliquam massa dolor, condimentum non, dapibus quis, vestibulum a, purus. Duis gravida porta dolor. Nullam sit amet mi sagittis massa pretium ultricies. Ut fringilla. Aliquam venenatis feugiat dui. Pellentesque tempor, leo et ullamcorper placerat, felis libero blandit nisl, sit amet pharetra nisl erat sit amet dolor. In sem erat, porta rhoncus, auctor ac, convallis feugiat, dui. Sed tempus metus at erat. Cras gravida. Cras urna nisi, fermentum non, sodales sed, adipiscing sed, nulla.
Donec pellentesque, velit sit amet ultricies dictum, sem quam posuere arcu, tristique mollis nibh dui ac libero. Vestibulum consectetur velit sed dolor. Nullam ornare sodales nisi. Sed facilisis sollicitudin arcu. Proin vitae lectus vel nulla auctor faucibus. Aenean nec nisl sit amet risus faucibus tincidunt. Ut sed nisl. Vivamus vehicula ligula a nunc. Curabitur mauris. Integer est felis, fermentum aliquam, dignissim id, gravida id, purus. Nullam porta felis eget ante. Phasellus lacinia, odio et elementum viverra, dui neque tempus massa, ac adipiscing lectus velit vitae turpis. Integer scelerisque neque in tortor. Duis et elit. Mauris elementum urna sed risus. Nunc aliquet, ipsum ac venenatis elementum, sapien orci vulputate lacus, eu lobortis sapien lorem non enim. Donec enim sapien, gravida vitae, vestibulum et, mattis dapibus, sem. Phasellus mi.
Cras a lorem nec diam aliquet viverra. Aliquam sed leo sit amet neque porta venenatis. Nulla facilisi. Duis lectus felis, fermentum quis, sagittis a, vestibulum consectetur, orci. Suspendisse sodales odio vitae lectus. Curabitur nunc. Phasellus cursus diam sit amet ipsum. Aenean cursus tellus eget nunc. Vivamus mattis urna a lorem. Mauris vitae mi. Morbi luctus molestie pede. Vestibulum lobortis lacus sed lacus. Aliquam nisl libero, pellentesque sed, facilisis ut, varius vehicula, lacus. Mauris orci. Pellentesque sed risus id quam sodales lobortis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed facilisis, odio vitae laoreet auctor, nulla dui sagittis lectus, sit amet viverra orci arcu et lectus. Pellentesque ut sem. Maecenas a est.
Suspendisse tincidunt. Sed bibendum molestie ligula. Suspendisse massa mauris, cursus a, luctus eget, posuere sed, nulla. Fusce vitae urna. Duis fermentum. Donec a neque ac pede lobortis mollis. Donec massa ligula, facilisis et, dapibus sed, ultrices non, sem. Phasellus diam. Maecenas velit arcu, viverra ut, gravida ac, tincidunt eget, nunc. Sed sit amet enim non dui tristique lacinia. Vivamus eu orci. Duis metus. Nulla facilisi. Phasellus at lectus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus mauris. Duis dictum, erat eu scelerisque sollicitudin, mi augue tincidunt lorem, vel lacinia lectus nibh quis dui. Ut ornare dignissim elit. Phasellus posuere ligula vel justo.
Sed neque. Quisque porttitor lorem vel arcu. Praesent a mi et risus suscipit egestas. Proin est lectus, rhoncus sit amet, aliquet non, fermentum ut, nulla. Aenean erat nisl, rutrum dictum, placerat vel, malesuada ac, arcu. Suspendisse posuere, ante sed consequat gravida, velit mi dignissim sem, et pretium dui diam in elit. Sed dictum nulla. Duis tristique tincidunt eros. Morbi velit. Donec a lorem. Aliquam cursus. Vivamus dapibus varius neque. Quisque eget diam. Nulla facilisi. Pellentesque eu sapien. Curabitur egestas cursus risus. Proin odio tortor, interdum eget, fringilla venenatis, luctus et, turpis. Phasellus nulla lorem, ullamcorper ut, elementum rhoncus, ultrices sed, ipsum. Nam volutpat.
Quisque eu purus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur non nulla. Donec dignissim porta lorem. Mauris ornare venenatis odio. Aenean eu lectus at eros porta molestie. Vivamus neque. Morbi aliquam. Nulla tincidunt enim id ligula. Nunc gravida felis consectetur dolor. Phasellus tempus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla nec libero quis ipsum auctor vehicula. Maecenas consectetur tempor turpis. Integer commodo. In hac habitasse platea dictumst. Donec nec justo quis sem placerat gravida. Donec sollicitudin, turpis sit amet bibendum elementum, nisl ligula venenatis ipsum, vel sollicitudin justo arcu eu leo. Phasellus aliquet faucibus libero.
Nam molestie nulla eu risus. Fusce id erat. Maecenas volutpat nunc eu ligula. Sed tortor est, imperdiet quis, viverra et, ornare in, ipsum. Praesent aliquet. Nulla suscipit. In posuere enim in odio. Nullam ante ipsum, imperdiet id, convallis id, hendrerit id, libero. Donec convallis libero ut felis. Curabitur venenatis placerat justo. Vestibulum mauris arcu, scelerisque et, dapibus vel, scelerisque a, lectus. Sed sit amet orci id felis tristique tincidunt. Curabitur adipiscing, nulla vel pretium tristique, tellus ipsum lacinia eros, vitae venenatis pede lectus sit amet neque. Nulla feugiat. Aenean euismod, nunc vel rutrum mattis, lorem purus venenatis sapien, sed facilisis pede enim at elit. Vivamus viverra. Cras vitae purus ac felis fringilla condimentum. Nullam ac nisi vitae massa faucibus aliquet.
Etiam ut nibh nec neque elementum iaculis. Maecenas facilisis nulla at lorem. Pellentesque elit sapien, ultricies at, dignissim nec, cursus eu, pede. Morbi gravida, ipsum eget malesuada eleifend, est arcu pulvinar nisl, et fermentum urna felis sed tellus. Mauris fermentum sollicitudin neque. Nunc at odio. Pellentesque et turpis. Cras congue congue nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam feugiat dapibus tellus. Aenean aliquam, mi id interdum feugiat, nunc lorem laoreet massa, ac semper urna sem vel purus. Quisque bibendum tellus ut est.
Morbi aliquam lectus sit amet nisi. Praesent urna purus, porttitor vitae, accumsan in, sollicitudin at, lorem. Donec diam dui, hendrerit eget, tincidunt ut, ultrices vehicula, dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam erat volutpat. Pellentesque iaculis iaculis lacus. Fusce auctor. Morbi vulputate purus at felis. Maecenas a pede nec metus vulputate ullamcorper. Cras non lectus. Suspendisse lacinia.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

32
deps/zlib/google/test_data.filelist vendored Normal file
View File

@ -0,0 +1,32 @@
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# NOTE: this file is generated by build/ios/update_bundle_filelist.py
# If it requires updating, you should get a presubmit error with
# instructions on how to regenerate. Otherwise, do not edit.
test/data/Different Encryptions.zip
test/data/Empty Dir Same Name As File.zip
test/data/Mixed Paths.zip
test/data/Parent Dir Same Name As File.zip
test/data/README.md
test/data/Repeated Dir Name.zip
test/data/Repeated File Name With Different Cases.zip
test/data/Repeated File Name.zip
test/data/SJIS Bug 846195.zip
test/data/Windows Special Names.zip
test/data/Wrong CRC.zip
test/data/create_test_zip.sh
test/data/empty.zip
test/data/evil.zip
test/data/evil_via_absolute_file_name.zip
test/data/evil_via_invalid_utf8.zip
test/data/test.zip
test/data/test/foo.txt
test/data/test/foo/bar.txt
test/data/test/foo/bar/.hidden
test/data/test/foo/bar/baz.txt
test/data/test/foo/bar/quux.txt
test/data/test_encrypted.zip
test/data/test_mismatch_size.zip
test/data/test_nocompress.zip
test/data/test_posix_permissions.zip

8
deps/zlib/google/test_data.globlist vendored Normal file
View File

@ -0,0 +1,8 @@
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# See build/ios/update_bundle_filelist.py for details on how .globlist
# files are used to update their .filelist counterparts.
test/data/**
test/data/test/foo/bar/.hidden

274
deps/zlib/google/zip.cc vendored Normal file
View File

@ -0,0 +1,274 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip.h"
#include <string>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "third_party/zlib/google/redact.h"
#include "third_party/zlib/google/zip_internal.h"
#include "third_party/zlib/google/zip_reader.h"
#include "third_party/zlib/google/zip_writer.h"
namespace zip {
namespace {
bool IsHiddenFile(const base::FilePath& file_path) {
return file_path.BaseName().value()[0] == '.';
}
// Creates a directory at |extract_dir|/|entry_path|, including any parents.
bool CreateDirectory(const base::FilePath& extract_dir,
const base::FilePath& entry_path) {
const base::FilePath dir = extract_dir.Append(entry_path);
const bool ok = base::CreateDirectory(dir);
PLOG_IF(ERROR, !ok) << "Cannot create directory " << Redact(dir);
return ok;
}
// Creates a WriterDelegate that can write a file at |extract_dir|/|entry_path|.
std::unique_ptr<WriterDelegate> CreateFilePathWriterDelegate(
const base::FilePath& extract_dir,
const base::FilePath& entry_path) {
return std::make_unique<FilePathWriterDelegate>(
extract_dir.Append(entry_path));
}
class DirectFileAccessor : public FileAccessor {
public:
explicit DirectFileAccessor(base::FilePath src_dir)
: src_dir_(std::move(src_dir)) {}
~DirectFileAccessor() override = default;
bool Open(const Paths paths, std::vector<base::File>* const files) override {
DCHECK(files);
files->reserve(files->size() + paths.size());
for (const base::FilePath& path : paths) {
DCHECK(!path.IsAbsolute());
const base::FilePath absolute_path = src_dir_.Append(path);
if (base::DirectoryExists(absolute_path)) {
files->emplace_back();
LOG(ERROR) << "Cannot open " << Redact(path) << ": It is a directory";
} else {
const base::File& file = files->emplace_back(
absolute_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
LOG_IF(ERROR, !file.IsValid())
<< "Cannot open " << Redact(path) << ": "
<< base::File::ErrorToString(file.error_details());
}
}
return true;
}
bool List(const base::FilePath& path,
std::vector<base::FilePath>* const files,
std::vector<base::FilePath>* const subdirs) override {
DCHECK(!path.IsAbsolute());
DCHECK(files);
DCHECK(subdirs);
base::FileEnumerator file_enumerator(
src_dir_.Append(path), false /* recursive */,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
while (!file_enumerator.Next().empty()) {
const base::FileEnumerator::FileInfo info = file_enumerator.GetInfo();
(info.IsDirectory() ? subdirs : files)
->push_back(path.Append(info.GetName()));
}
return true;
}
bool GetInfo(const base::FilePath& path, Info* const info) override {
DCHECK(!path.IsAbsolute());
DCHECK(info);
base::File::Info file_info;
if (!base::GetFileInfo(src_dir_.Append(path), &file_info)) {
PLOG(ERROR) << "Cannot get info of " << Redact(path);
return false;
}
info->is_directory = file_info.is_directory;
info->last_modified = file_info.last_modified;
return true;
}
private:
const base::FilePath src_dir_;
};
} // namespace
std::ostream& operator<<(std::ostream& out, const Progress& progress) {
return out << progress.bytes << " bytes, " << progress.files << " files, "
<< progress.directories << " dirs, " << progress.errors
<< " errors";
}
bool Zip(const ZipParams& params) {
DirectFileAccessor default_accessor(params.src_dir);
FileAccessor* const file_accessor = params.file_accessor ?: &default_accessor;
std::unique_ptr<internal::ZipWriter> zip_writer;
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
if (params.dest_fd != base::kInvalidPlatformFile) {
DCHECK(params.dest_file.empty());
zip_writer =
internal::ZipWriter::CreateWithFd(params.dest_fd, file_accessor);
if (!zip_writer)
return false;
}
#endif
if (!zip_writer) {
zip_writer = internal::ZipWriter::Create(params.dest_file, file_accessor);
if (!zip_writer)
return false;
}
zip_writer->SetProgressCallback(params.progress_callback,
params.progress_period);
zip_writer->SetRecursive(params.recursive);
zip_writer->ContinueOnError(params.continue_on_error);
if (!params.include_hidden_files || params.filter_callback)
zip_writer->SetFilterCallback(base::BindRepeating(
[](const ZipParams* const params, const base::FilePath& path) -> bool {
return (params->include_hidden_files || !IsHiddenFile(path)) &&
(!params->filter_callback ||
params->filter_callback.Run(params->src_dir.Append(path)));
},
&params));
if (params.src_files.empty()) {
// No source items are specified. Zip the entire source directory.
zip_writer->SetRecursive(true);
if (!zip_writer->AddDirectoryContents(base::FilePath()))
return false;
} else {
// Only zip the specified source items.
if (!zip_writer->AddMixedEntries(params.src_files))
return false;
}
return zip_writer->Close();
}
bool Unzip(const base::FilePath& src_file,
const base::FilePath& dest_dir,
UnzipOptions options) {
base::File file(src_file, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
PLOG(ERROR) << "Cannot open " << Redact(src_file) << ": "
<< base::File::ErrorToString(file.error_details());
return false;
}
DLOG_IF(WARNING, !base::IsDirectoryEmpty(dest_dir))
<< "ZIP extraction directory is not empty: " << dest_dir;
return Unzip(file.GetPlatformFile(),
base::BindRepeating(&CreateFilePathWriterDelegate, dest_dir),
base::BindRepeating(&CreateDirectory, dest_dir),
std::move(options));
}
bool Unzip(const base::PlatformFile& src_file,
WriterFactory writer_factory,
DirectoryCreator directory_creator,
UnzipOptions options) {
ZipReader reader;
reader.SetEncoding(std::move(options.encoding));
reader.SetPassword(std::move(options.password));
if (!reader.OpenFromPlatformFile(src_file)) {
LOG(ERROR) << "Cannot open ZIP from file handle " << src_file;
return false;
}
while (const ZipReader::Entry* const entry = reader.Next()) {
if (entry->is_unsafe) {
LOG(ERROR) << "Found unsafe entry " << Redact(entry->path) << " in ZIP";
if (!options.continue_on_error)
return false;
continue;
}
if (options.filter && !options.filter.Run(entry->path)) {
VLOG(1) << "Skipped ZIP entry " << Redact(entry->path);
continue;
}
if (entry->is_directory) {
// It's a directory.
if (!directory_creator.Run(entry->path)) {
LOG(ERROR) << "Cannot create directory " << Redact(entry->path);
if (!options.continue_on_error)
return false;
}
continue;
}
// It's a file.
std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry->path);
if (!writer ||
(options.progress ? !reader.ExtractCurrentEntryWithListener(
writer.get(), options.progress)
: !reader.ExtractCurrentEntry(writer.get()))) {
LOG(ERROR) << "Cannot extract file " << Redact(entry->path)
<< " from ZIP";
if (!options.continue_on_error)
return false;
}
}
return reader.ok();
}
bool ZipWithFilterCallback(const base::FilePath& src_dir,
const base::FilePath& dest_file,
FilterCallback filter) {
DCHECK(base::DirectoryExists(src_dir));
return Zip({.src_dir = src_dir,
.dest_file = dest_file,
.filter_callback = std::move(filter)});
}
bool Zip(const base::FilePath& src_dir,
const base::FilePath& dest_file,
bool include_hidden_files) {
return Zip({.src_dir = src_dir,
.dest_file = dest_file,
.include_hidden_files = include_hidden_files});
}
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
bool ZipFiles(const base::FilePath& src_dir,
Paths src_relative_paths,
int dest_fd) {
DCHECK(base::DirectoryExists(src_dir));
return Zip({.src_dir = src_dir,
.dest_fd = dest_fd,
.src_files = src_relative_paths});
}
#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
} // namespace zip

219
deps/zlib/google/zip.h vendored Normal file
View File

@ -0,0 +1,219 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_H_
#define THIRD_PARTY_ZLIB_GOOGLE_ZIP_H_
#include <cstdint>
#include <ostream>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/platform_file.h"
#include "base/functional/callback.h"
#include "base/time/time.h"
#include "build/build_config.h"
namespace base {
class File;
}
namespace zip {
class WriterDelegate;
// Paths passed as span to avoid copying them.
using Paths = base::span<const base::FilePath>;
// Abstraction for file access operation required by Zip().
//
// Can be passed to the ZipParams for providing custom access to the files,
// for example over IPC.
//
// All parameters paths are expected to be relative to the source directory.
class FileAccessor {
public:
virtual ~FileAccessor() = default;
struct Info {
bool is_directory = false;
base::Time last_modified;
};
// Opens files specified in |paths|.
// Directories should be mapped to invalid files.
virtual bool Open(Paths paths, std::vector<base::File>* files) = 0;
// Lists contents of a directory at |path|.
virtual bool List(const base::FilePath& path,
std::vector<base::FilePath>* files,
std::vector<base::FilePath>* subdirs) = 0;
// Gets info about a file or directory.
virtual bool GetInfo(const base::FilePath& path, Info* info) = 0;
};
// Progress of a ZIP creation operation.
struct Progress {
// Total number of bytes read from files getting zipped so far.
std::int64_t bytes = 0;
// Number of file entries added to the ZIP so far.
// A file entry is added after its bytes have been processed.
int files = 0;
// Number of directory entries added to the ZIP so far.
// A directory entry is added before items in it.
int directories = 0;
// Number of errors encountered so far (files that cannot be opened,
// directories that cannot be listed).
int errors = 0;
};
// Prints Progress to output stream.
std::ostream& operator<<(std::ostream& out, const Progress& progress);
// Callback reporting the progress of a ZIP creation operation.
//
// This callback returns a boolean indicating whether the ZIP creation operation
// should continue. If it returns false once, then the ZIP creation operation is
// immediately cancelled and the callback won't be called again.
using ProgressCallback = base::RepeatingCallback<bool(const Progress&)>;
using FilterCallback = base::RepeatingCallback<bool(const base::FilePath&)>;
// ZIP creation parameters and options.
struct ZipParams {
// Source directory. Ignored if |file_accessor| is set.
base::FilePath src_dir;
// Abstraction around file system access used to read files.
// If left null, an implementation that accesses files directly is used.
FileAccessor* file_accessor = nullptr; // Not owned
// Destination file path.
// Either dest_file or dest_fd should be set, but not both.
base::FilePath dest_file;
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Destination file passed a file descriptor.
// Either dest_file or dest_fd should be set, but not both.
int dest_fd = base::kInvalidPlatformFile;
#endif
// The relative paths to the files and directories that should be included in
// the ZIP file. If this is empty, the whole contents of |src_dir| are
// included.
//
// These paths must be relative to |src_dir| and will be used as the file
// names in the created ZIP file. All files must be under |src_dir| in the
// file system hierarchy.
//
// All the paths in |src_files| are included in the created ZIP file,
// irrespective of |include_hidden_files| and |filter_callback|.
Paths src_files;
// Filter used to exclude files from the ZIP file. This is only taken in
// account when recursively adding subdirectory contents.
FilterCallback filter_callback;
// Optional progress reporting callback.
ProgressCallback progress_callback;
// Progress reporting period. The final callback is always called when the ZIP
// creation operation completes.
base::TimeDelta progress_period;
// Should add hidden files? This is only taken in account when recursively
// adding subdirectory contents.
bool include_hidden_files = true;
// Should recursively add subdirectory contents?
bool recursive = false;
// Should ignore errors when discovering files and zipping them?
bool continue_on_error = false;
};
// Zip files specified into a ZIP archives. The source files and ZIP destination
// files (as well as other settings) are specified in |params|.
bool Zip(const ZipParams& params);
// Zip the contents of src_dir into dest_file. src_path must be a directory.
// An entry will *not* be created in the zip for the root folder -- children
// of src_dir will be at the root level of the created zip. For each file in
// src_dir, include it only if the callback |filter_cb| returns true. Otherwise
// omit it.
bool ZipWithFilterCallback(const base::FilePath& src_dir,
const base::FilePath& dest_file,
FilterCallback filter_cb);
// Convenience method for callers who don't need to set up the filter callback.
// If |include_hidden_files| is true, files starting with "." are included.
// Otherwise they are omitted.
bool Zip(const base::FilePath& src_dir,
const base::FilePath& dest_file,
bool include_hidden_files);
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Zips files listed in |src_relative_paths| to destination specified by file
// descriptor |dest_fd|, without taking ownership of |dest_fd|. The paths listed
// in |src_relative_paths| are relative to the |src_dir| and will be used as the
// file names in the created zip file. All source paths must be under |src_dir|
// in the file system hierarchy.
bool ZipFiles(const base::FilePath& src_dir,
Paths src_relative_paths,
int dest_fd);
#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
// Callback reporting the number of bytes written during Unzip.
using UnzipProgressCallback = base::RepeatingCallback<void(uint64_t bytes)>;
// Options of the Unzip function, with valid default values.
struct UnzipOptions {
// Encoding of entry paths in the ZIP archive. By default, paths are assumed
// to be in UTF-8.
std::string encoding;
// Only extract the entries for which |filter_cb| returns true. By default,
// everything gets extracted.
FilterCallback filter;
// Callback to report bytes extracted from the ZIP.
UnzipProgressCallback progress;
// Password to decrypt the encrypted files.
std::string password;
// Should ignore errors when extracting files?
bool continue_on_error = false;
};
typedef base::RepeatingCallback<std::unique_ptr<WriterDelegate>(
const base::FilePath&)>
WriterFactory;
typedef base::RepeatingCallback<bool(const base::FilePath&)> DirectoryCreator;
// Unzips the contents of |zip_file|, using the writers provided by
// |writer_factory|.
bool Unzip(const base::PlatformFile& zip_file,
WriterFactory writer_factory,
DirectoryCreator directory_creator,
UnzipOptions options = {});
// Unzips the contents of |zip_file| into |dest_dir|.
// This function does not overwrite any existing file.
// A filename collision will result in an error.
// Therefore, |dest_dir| should initially be an empty directory.
bool Unzip(const base::FilePath& zip_file,
const base::FilePath& dest_dir,
UnzipOptions options = {});
} // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_H_

465
deps/zlib/google/zip_internal.cc vendored Normal file
View File

@ -0,0 +1,465 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip_internal.h"
#include <stddef.h>
#include <string.h>
#include <algorithm>
#include <string_view>
#include "base/containers/fixed_flat_set.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#if defined(USE_SYSTEM_MINIZIP)
#include <minizip/ioapi.h>
#include <minizip/unzip.h>
#include <minizip/zip.h>
#else
#include "third_party/zlib/contrib/minizip/unzip.h"
#include "third_party/zlib/contrib/minizip/zip.h"
#if defined(OS_WIN)
#include "third_party/zlib/contrib/minizip/iowin32.h"
#elif defined(OS_POSIX)
#include "third_party/zlib/contrib/minizip/ioapi.h"
#endif // defined(OS_POSIX)
#endif // defined(USE_SYSTEM_MINIZIP)
namespace {
#if defined(OS_WIN)
typedef struct {
HANDLE hf;
int error;
} WIN32FILE_IOWIN;
// This function is derived from third_party/minizip/iowin32.c.
// Its only difference is that it treats the filename as UTF-8 and
// uses the Unicode version of CreateFile.
void* ZipOpenFunc(void* opaque, const void* filename, int mode) {
DWORD desired_access = 0, creation_disposition = 0;
DWORD share_mode = 0, flags_and_attributes = 0;
HANDLE file = 0;
void* ret = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
desired_access = GENERIC_READ;
creation_disposition = OPEN_EXISTING;
share_mode = FILE_SHARE_READ;
} else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
desired_access = GENERIC_WRITE | GENERIC_READ;
creation_disposition = OPEN_EXISTING;
} else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
desired_access = GENERIC_WRITE | GENERIC_READ;
creation_disposition = CREATE_ALWAYS;
}
if (filename != nullptr && desired_access != 0) {
file = CreateFileW(
base::UTF8ToWide(static_cast<const char*>(filename)).c_str(),
desired_access, share_mode, nullptr, creation_disposition,
flags_and_attributes, nullptr);
}
if (file == INVALID_HANDLE_VALUE)
file = NULL;
if (file != NULL) {
WIN32FILE_IOWIN file_ret;
file_ret.hf = file;
file_ret.error = 0;
ret = malloc(sizeof(WIN32FILE_IOWIN));
if (ret == NULL)
CloseHandle(file);
else
*(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
}
return ret;
}
#endif
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Callback function for zlib that opens a file stream from a file descriptor.
// Since we do not own the file descriptor, dup it so that we can fdopen/fclose
// a file stream.
void* FdOpenFileFunc(void* opaque, const void* filename, int mode) {
FILE* file = NULL;
const char* mode_fopen = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
mode_fopen = "rb";
else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
mode_fopen = "r+b";
else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
mode_fopen = "wb";
if ((filename != NULL) && (mode_fopen != NULL)) {
int fd = dup(*static_cast<int*>(opaque));
if (fd != -1)
file = fdopen(fd, mode_fopen);
}
return file;
}
int FdCloseFileFunc(void* opaque, void* stream) {
fclose(static_cast<FILE*>(stream));
free(opaque); // malloc'ed in FillFdOpenFileFunc()
return 0;
}
// Fills |pzlib_filecunc_def| appropriately to handle the zip file
// referred to by |fd|.
void FillFdOpenFileFunc(zlib_filefunc64_def* pzlib_filefunc_def, int fd) {
fill_fopen64_filefunc(pzlib_filefunc_def);
pzlib_filefunc_def->zopen64_file = FdOpenFileFunc;
pzlib_filefunc_def->zclose_file = FdCloseFileFunc;
int* ptr_fd = static_cast<int*>(malloc(sizeof(fd)));
*ptr_fd = fd;
pzlib_filefunc_def->opaque = ptr_fd;
}
#endif // defined(OS_POSIX)
#if defined(OS_WIN)
// Callback function for zlib that opens a file stream from a Windows handle.
// Does not take ownership of the handle.
void* HandleOpenFileFunc(void* opaque, const void* /*filename*/, int mode) {
WIN32FILE_IOWIN file_ret;
file_ret.hf = static_cast<HANDLE>(opaque);
file_ret.error = 0;
if (file_ret.hf == INVALID_HANDLE_VALUE)
return NULL;
void* ret = malloc(sizeof(WIN32FILE_IOWIN));
if (ret != NULL)
*(static_cast<WIN32FILE_IOWIN*>(ret)) = file_ret;
return ret;
}
int HandleCloseFileFunc(void* opaque, void* stream) {
free(stream); // malloc'ed in HandleOpenFileFunc()
return 0;
}
#endif
// A struct that contains data required for zlib functions to extract files from
// a zip archive stored in memory directly. The following I/O API functions
// expect their opaque parameters refer to this struct.
struct ZipBuffer {
const char* data; // weak
ZPOS64_T length;
ZPOS64_T offset;
};
// Opens the specified file. When this function returns a non-NULL pointer, zlib
// uses this pointer as a stream parameter while compressing or uncompressing
// data. (Returning NULL represents an error.) This function initializes the
// given opaque parameter and returns it because this parameter stores all
// information needed for uncompressing data. (This function does not support
// writing compressed data and it returns NULL for this case.)
void* OpenZipBuffer(void* opaque, const void* /*filename*/, int mode) {
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) {
NOTREACHED();
}
ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
if (!buffer || !buffer->data || !buffer->length)
return NULL;
buffer->offset = 0;
return opaque;
}
// Reads compressed data from the specified stream. This function copies data
// refered by the opaque parameter and returns the size actually copied.
uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) {
ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
DCHECK_LE(buffer->offset, buffer->length);
ZPOS64_T remaining_bytes = buffer->length - buffer->offset;
if (!buffer || !buffer->data || !remaining_bytes)
return 0;
if (size > remaining_bytes)
size = remaining_bytes;
memcpy(buf, &buffer->data[buffer->offset], size);
buffer->offset += size;
return size;
}
// Writes compressed data to the stream. This function always returns zero
// because this implementation is only for reading compressed data.
uLong WriteZipBuffer(void* /*opaque*/,
void* /*stream*/,
const void* /*buf*/,
uLong /*size*/) {
NOTREACHED();
}
// Returns the offset from the beginning of the data.
ZPOS64_T GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) {
ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
if (!buffer)
return -1;
return buffer->offset;
}
// Moves the current offset to the specified position.
long SeekZipBuffer(void* opaque,
void* /*stream*/,
ZPOS64_T offset,
int origin) {
ZipBuffer* buffer = static_cast<ZipBuffer*>(opaque);
if (!buffer)
return -1;
if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
buffer->offset = std::min(buffer->offset + offset, buffer->length);
return 0;
}
if (origin == ZLIB_FILEFUNC_SEEK_END) {
buffer->offset = (buffer->length > offset) ? buffer->length - offset : 0;
return 0;
}
if (origin == ZLIB_FILEFUNC_SEEK_SET) {
buffer->offset = std::min(buffer->length, offset);
return 0;
}
NOTREACHED();
}
// Closes the input offset and deletes all resources used for compressing or
// uncompressing data. This function deletes the ZipBuffer object referred by
// the opaque parameter since zlib deletes the unzFile object and it does not
// use this object any longer.
int CloseZipBuffer(void* opaque, void* /*stream*/) {
if (opaque)
free(opaque);
return 0;
}
// Returns the last error happened when reading or writing data. This function
// always returns zero, which means there are not any errors.
int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) {
return 0;
}
// Returns a zip_fileinfo struct with the time represented by |file_time|.
zip_fileinfo TimeToZipFileInfo(const base::Time& file_time) {
base::Time::Exploded file_time_parts;
file_time.UTCExplode(&file_time_parts);
zip_fileinfo zip_info = {};
if (file_time_parts.year >= 1980) {
// This if check works around the handling of the year value in
// contrib/minizip/zip.c in function zip64local_TmzDateToDosDate
// It assumes that dates below 1980 are in the double digit format.
// Hence the fail safe option is to leave the date unset. Some programs
// might show the unset date as 1980-0-0 which is invalid.
zip_info.tmz_date.tm_year = file_time_parts.year;
zip_info.tmz_date.tm_mon = file_time_parts.month - 1;
zip_info.tmz_date.tm_mday = file_time_parts.day_of_month;
zip_info.tmz_date.tm_hour = file_time_parts.hour;
zip_info.tmz_date.tm_min = file_time_parts.minute;
zip_info.tmz_date.tm_sec = file_time_parts.second;
}
return zip_info;
}
} // namespace
namespace zip {
namespace internal {
unzFile OpenForUnzipping(const std::string& file_name_utf8) {
zlib_filefunc64_def* zip_func_ptrs = nullptr;
#if defined(OS_WIN)
zlib_filefunc64_def zip_funcs;
fill_win32_filefunc64(&zip_funcs);
zip_funcs.zopen64_file = ZipOpenFunc;
zip_func_ptrs = &zip_funcs;
#endif
return unzOpen2_64(file_name_utf8.c_str(), zip_func_ptrs);
}
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
unzFile OpenFdForUnzipping(int zip_fd) {
zlib_filefunc64_def zip_funcs;
FillFdOpenFileFunc(&zip_funcs, zip_fd);
// Passing dummy "fd" filename to zlib.
return unzOpen2_64("fd", &zip_funcs);
}
#endif
#if defined(OS_WIN)
unzFile OpenHandleForUnzipping(HANDLE zip_handle) {
zlib_filefunc64_def zip_funcs;
fill_win32_filefunc64(&zip_funcs);
zip_funcs.zopen64_file = HandleOpenFileFunc;
zip_funcs.zclose_file = HandleCloseFileFunc;
zip_funcs.opaque = zip_handle;
return unzOpen2_64("fd", &zip_funcs);
}
#endif
// static
unzFile PrepareMemoryForUnzipping(const std::string& data) {
if (data.empty())
return NULL;
ZipBuffer* buffer = static_cast<ZipBuffer*>(malloc(sizeof(ZipBuffer)));
if (!buffer)
return NULL;
buffer->data = data.data();
buffer->length = data.length();
buffer->offset = 0;
zlib_filefunc64_def zip_functions;
zip_functions.zopen64_file = OpenZipBuffer;
zip_functions.zread_file = ReadZipBuffer;
zip_functions.zwrite_file = WriteZipBuffer;
zip_functions.ztell64_file = GetOffsetOfZipBuffer;
zip_functions.zseek64_file = SeekZipBuffer;
zip_functions.zclose_file = CloseZipBuffer;
zip_functions.zerror_file = GetErrorOfZipBuffer;
zip_functions.opaque = buffer;
return unzOpen2_64(nullptr, &zip_functions);
}
zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) {
zlib_filefunc64_def* zip_func_ptrs = nullptr;
#if defined(OS_WIN)
zlib_filefunc64_def zip_funcs;
fill_win32_filefunc64(&zip_funcs);
zip_funcs.zopen64_file = ZipOpenFunc;
zip_func_ptrs = &zip_funcs;
#endif
return zipOpen2_64(file_name_utf8.c_str(), append_flag, nullptr,
zip_func_ptrs);
}
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
zipFile OpenFdForZipping(int zip_fd, int append_flag) {
zlib_filefunc64_def zip_funcs;
FillFdOpenFileFunc(&zip_funcs, zip_fd);
// Passing dummy "fd" filename to zlib.
return zipOpen2_64("fd", append_flag, nullptr, &zip_funcs);
}
#endif
bool ZipOpenNewFileInZip(zipFile zip_file,
const std::string& str_path,
base::Time last_modified_time,
Compression compression) {
// Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
// Setting the Language encoding flag so the file is told to be in utf-8.
const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11;
const zip_fileinfo file_info = TimeToZipFileInfo(last_modified_time);
const int err = zipOpenNewFileInZip4_64(
/*file=*/zip_file,
/*filename=*/str_path.c_str(),
/*zip_fileinfo=*/&file_info,
/*extrafield_local=*/nullptr,
/*size_extrafield_local=*/0u,
/*extrafield_global=*/nullptr,
/*size_extrafield_global=*/0u,
/*comment=*/nullptr,
/*method=*/compression,
/*level=*/Z_DEFAULT_COMPRESSION,
/*raw=*/0,
/*windowBits=*/-MAX_WBITS,
/*memLevel=*/DEF_MEM_LEVEL,
/*strategy=*/Z_DEFAULT_STRATEGY,
/*password=*/nullptr,
/*crcForCrypting=*/0,
/*versionMadeBy=*/0,
/*flagBase=*/LANGUAGE_ENCODING_FLAG,
/*zip64=*/1);
if (err != ZIP_OK) {
DLOG(ERROR) << "Cannot open ZIP file entry '" << str_path
<< "': zipOpenNewFileInZip4_64 returned " << err;
return false;
}
return true;
}
Compression GetCompressionMethod(const base::FilePath& path) {
// Get the filename extension in lower case.
const base::FilePath::StringType ext =
base::ToLowerASCII(path.FinalExtension());
if (ext.empty())
return kDeflated;
// Skip the leading dot.
base::FilePath::StringViewType ext_without_dot = ext;
DCHECK_EQ(ext_without_dot.front(), FILE_PATH_LITERAL('.'));
ext_without_dot.remove_prefix(1);
// Well known filename extensions of files that a likely to be already
// compressed. The extensions are in lower case without the leading dot.
static constexpr auto kExts =
base::MakeFixedFlatSet<base::FilePath::StringViewType>({
FILE_PATH_LITERAL("3g2"), //
FILE_PATH_LITERAL("3gp"), //
FILE_PATH_LITERAL("7z"), //
FILE_PATH_LITERAL("7zip"), //
FILE_PATH_LITERAL("aac"), //
FILE_PATH_LITERAL("avi"), //
FILE_PATH_LITERAL("bz"), //
FILE_PATH_LITERAL("bz2"), //
FILE_PATH_LITERAL("crx"), //
FILE_PATH_LITERAL("gif"), //
FILE_PATH_LITERAL("gz"), //
FILE_PATH_LITERAL("jar"), //
FILE_PATH_LITERAL("jpeg"), //
FILE_PATH_LITERAL("jpg"), //
FILE_PATH_LITERAL("lz"), //
FILE_PATH_LITERAL("m2v"), //
FILE_PATH_LITERAL("m4p"), //
FILE_PATH_LITERAL("m4v"), //
FILE_PATH_LITERAL("mng"), //
FILE_PATH_LITERAL("mov"), //
FILE_PATH_LITERAL("mp2"), //
FILE_PATH_LITERAL("mp3"), //
FILE_PATH_LITERAL("mp4"), //
FILE_PATH_LITERAL("mpe"), //
FILE_PATH_LITERAL("mpeg"), //
FILE_PATH_LITERAL("mpg"), //
FILE_PATH_LITERAL("mpv"), //
FILE_PATH_LITERAL("ogg"), //
FILE_PATH_LITERAL("ogv"), //
FILE_PATH_LITERAL("png"), //
FILE_PATH_LITERAL("qt"), //
FILE_PATH_LITERAL("rar"), //
FILE_PATH_LITERAL("taz"), //
FILE_PATH_LITERAL("tb2"), //
FILE_PATH_LITERAL("tbz"), //
FILE_PATH_LITERAL("tbz2"), //
FILE_PATH_LITERAL("tgz"), //
FILE_PATH_LITERAL("tlz"), //
FILE_PATH_LITERAL("tz"), //
FILE_PATH_LITERAL("tz2"), //
FILE_PATH_LITERAL("vob"), //
FILE_PATH_LITERAL("webm"), //
FILE_PATH_LITERAL("wma"), //
FILE_PATH_LITERAL("wmv"), //
FILE_PATH_LITERAL("xz"), //
FILE_PATH_LITERAL("z"), //
FILE_PATH_LITERAL("zip"), //
});
if (kExts.count(ext_without_dot)) {
return kStored;
}
return kDeflated;
}
} // namespace internal
} // namespace zip

88
deps/zlib/google/zip_internal.h vendored Normal file
View File

@ -0,0 +1,88 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_INTERNAL_H_
#define THIRD_PARTY_ZLIB_GOOGLE_ZIP_INTERNAL_H_
#include <string>
#include "base/time/time.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
#if defined(USE_SYSTEM_MINIZIP)
#include <minizip/unzip.h>
#include <minizip/zip.h>
#else
#include "third_party/zlib/contrib/minizip/unzip.h"
#include "third_party/zlib/contrib/minizip/zip.h"
#endif
namespace base {
class FilePath;
}
// Utility functions and constants used internally for the zip file
// library in the directory. Don't use them outside of the library.
namespace zip {
namespace internal {
// Opens the given file name in UTF-8 for unzipping, with some setup for
// Windows.
unzFile OpenForUnzipping(const std::string& file_name_utf8);
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Opens the file referred to by |zip_fd| for unzipping.
unzFile OpenFdForUnzipping(int zip_fd);
#endif
#if defined(OS_WIN)
// Opens the file referred to by |zip_handle| for unzipping.
unzFile OpenHandleForUnzipping(HANDLE zip_handle);
#endif
// Creates a custom unzFile object which reads data from the specified string.
// This custom unzFile object overrides the I/O API functions of zlib so it can
// read data from the specified string.
unzFile PrepareMemoryForUnzipping(const std::string& data);
// Opens the given file name in UTF-8 for zipping, with some setup for
// Windows. |append_flag| will be passed to zipOpen2().
zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag);
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Opens the file referred to by |zip_fd| for zipping. |append_flag| will be
// passed to zipOpen2().
zipFile OpenFdForZipping(int zip_fd, int append_flag);
#endif
// Compression methods.
enum Compression {
kStored = 0, // Stored (no compression)
kDeflated = Z_DEFLATED, // Deflated
};
// Adds a file (or directory) entry to the ZIP archive.
bool ZipOpenNewFileInZip(zipFile zip_file,
const std::string& str_path,
base::Time last_modified_time,
Compression compression);
// Selects the best compression method for the given file. The heuristic is
// based on the filename extension. By default, the compression method is
// kDeflated. But if the given path has an extension indicating a well known
// file format which is likely to be already compressed (eg ZIP, RAR, JPG,
// PNG...) then the compression method is simply kStored.
Compression GetCompressionMethod(const base::FilePath& path);
const int kZipMaxPath = 256;
const int kZipBufSize = 8192;
} // namespace internal
} // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_INTERNAL_H_

723
deps/zlib/google/zip_reader.cc vendored Normal file
View File

@ -0,0 +1,723 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip_reader.h"
#include <algorithm>
#include <string_view>
#include <utility>
#include "base/check.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "third_party/zlib/google/redact.h"
#include "third_party/zlib/google/zip_internal.h"
#if defined(USE_SYSTEM_MINIZIP)
#include <minizip/unzip.h>
#else
#include "third_party/zlib/contrib/minizip/unzip.h"
#if defined(OS_WIN)
#include "third_party/zlib/contrib/minizip/iowin32.h"
#endif // defined(OS_WIN)
#endif // defined(USE_SYSTEM_MINIZIP)
#if defined(OS_POSIX)
#include <sys/stat.h>
#endif
namespace zip {
namespace {
enum UnzipError : int;
std::ostream& operator<<(std::ostream& out, UnzipError error) {
#define SWITCH_ERR(X) \
case X: \
return out << #X;
switch (error) {
SWITCH_ERR(UNZ_OK);
SWITCH_ERR(UNZ_END_OF_LIST_OF_FILE);
SWITCH_ERR(UNZ_ERRNO);
SWITCH_ERR(UNZ_PARAMERROR);
SWITCH_ERR(UNZ_BADZIPFILE);
SWITCH_ERR(UNZ_INTERNALERROR);
SWITCH_ERR(UNZ_CRCERROR);
default:
return out << "UNZ" << static_cast<int>(error);
}
#undef SWITCH_ERR
}
bool IsValidFileNameCharacterOnWindows(char16_t c) {
if (c < 32)
return false;
switch (c) {
case '<': // Less than
case '>': // Greater than
case ':': // Colon
case '"': // Double quote
case '|': // Vertical bar or pipe
case '?': // Question mark
case '*': // Asterisk
case '/': // Forward slash
case '\\': // Backslash
return false;
}
return true;
}
// A writer delegate that writes to a given string.
class StringWriterDelegate : public WriterDelegate {
public:
explicit StringWriterDelegate(std::string* output) : output_(output) {}
// WriterDelegate methods:
bool WriteBytes(const char* data, int num_bytes) override {
output_->append(data, num_bytes);
return true;
}
private:
std::string* const output_;
};
#if defined(OS_POSIX)
void SetPosixFilePermissions(int fd, int mode) {
base::stat_wrapper_t sb;
if (base::File::Fstat(fd, &sb)) {
return;
}
mode_t new_mode = sb.st_mode;
// Transfer the executable bit only if the file is readable.
if ((sb.st_mode & S_IRUSR) == S_IRUSR && (mode & S_IXUSR) == S_IXUSR) {
new_mode |= S_IXUSR;
}
if ((sb.st_mode & S_IRGRP) == S_IRGRP && (mode & S_IXGRP) == S_IXGRP) {
new_mode |= S_IXGRP;
}
if ((sb.st_mode & S_IROTH) == S_IROTH && (mode & S_IXOTH) == S_IXOTH) {
new_mode |= S_IXOTH;
}
if (new_mode != sb.st_mode) {
fchmod(fd, new_mode);
}
}
#endif
} // namespace
ZipReader::ZipReader() {
Reset();
}
ZipReader::~ZipReader() {
Close();
}
bool ZipReader::Open(const base::FilePath& zip_path) {
DCHECK(!zip_file_);
// Use of "Unsafe" function does not look good, but there is no way to do
// this safely on Linux. See file_util.h for details.
zip_file_ = internal::OpenForUnzipping(zip_path.AsUTF8Unsafe());
if (!zip_file_) {
LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path);
return false;
}
return OpenInternal();
}
bool ZipReader::OpenFromPlatformFile(base::PlatformFile zip_fd) {
DCHECK(!zip_file_);
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
zip_file_ = internal::OpenFdForUnzipping(zip_fd);
#elif defined(OS_WIN)
zip_file_ = internal::OpenHandleForUnzipping(zip_fd);
#endif
if (!zip_file_) {
LOG(ERROR) << "Cannot open ZIP from file handle " << zip_fd;
return false;
}
return OpenInternal();
}
bool ZipReader::OpenFromString(const std::string& data) {
zip_file_ = internal::PrepareMemoryForUnzipping(data);
if (!zip_file_)
return false;
return OpenInternal();
}
void ZipReader::Close() {
if (zip_file_) {
if (const UnzipError err{unzClose(zip_file_)}; err != UNZ_OK) {
LOG(ERROR) << "Error while closing ZIP archive: " << err;
}
}
Reset();
}
const ZipReader::Entry* ZipReader::Next() {
DCHECK(zip_file_);
if (reached_end_)
return nullptr;
DCHECK(ok_);
// Move to the next entry if we're not trying to open the first entry.
if (next_index_ > 0) {
if (const UnzipError err{unzGoToNextFile(zip_file_)}; err != UNZ_OK) {
reached_end_ = true;
if (err != UNZ_END_OF_LIST_OF_FILE) {
LOG(ERROR) << "Cannot go to next entry in ZIP: " << err;
ok_ = false;
}
return nullptr;
}
}
next_index_++;
if (!OpenEntry()) {
reached_end_ = true;
ok_ = false;
return nullptr;
}
return &entry_;
}
bool ZipReader::OpenEntry() {
DCHECK(zip_file_);
// Get entry info.
unz_file_info64 info = {};
char path_in_zip[internal::kZipMaxPath] = {};
if (const UnzipError err{unzGetCurrentFileInfo64(
zip_file_, &info, path_in_zip, sizeof(path_in_zip) - 1, nullptr, 0,
nullptr, 0)};
err != UNZ_OK) {
LOG(ERROR) << "Cannot get entry from ZIP: " << err;
return false;
}
entry_.path_in_original_encoding = path_in_zip;
// Convert path from original encoding to Unicode.
std::u16string path_in_utf16;
const char* const encoding = encoding_.empty() ? "UTF-8" : encoding_.c_str();
if (!base::CodepageToUTF16(entry_.path_in_original_encoding, encoding,
base::OnStringConversionError::SUBSTITUTE,
&path_in_utf16)) {
LOG(ERROR) << "Cannot convert path from encoding " << encoding;
return false;
}
// Normalize path.
Normalize(path_in_utf16);
entry_.original_size = info.uncompressed_size;
// The file content of this entry is encrypted if flag bit 0 is set.
entry_.is_encrypted = info.flag & 1;
if (entry_.is_encrypted) {
// Is the entry AES encrypted.
entry_.uses_aes_encryption = info.compression_method == 99;
} else {
entry_.uses_aes_encryption = false;
}
// Construct the last modified time. The timezone info is not present in ZIP
// archives, so we construct the time as UTC.
const base::Time::Exploded exploded_time = {
.year = static_cast<int>(info.tmu_date.tm_year),
.month =
static_cast<int>(info.tmu_date.tm_mon + 1), // 0-based vs 1-based
.day_of_month = static_cast<int>(info.tmu_date.tm_mday),
.hour = static_cast<int>(info.tmu_date.tm_hour),
.minute = static_cast<int>(info.tmu_date.tm_min),
.second = static_cast<int>(info.tmu_date.tm_sec)};
if (!base::Time::FromUTCExploded(exploded_time, &entry_.last_modified))
entry_.last_modified = base::Time::UnixEpoch();
#if defined(OS_POSIX)
entry_.posix_mode = (info.external_fa >> 16L) & (S_IRWXU | S_IRWXG | S_IRWXO);
#else
entry_.posix_mode = 0;
#endif
return true;
}
void ZipReader::Normalize(std::u16string_view in) {
entry_.is_unsafe = true;
// Directory entries in ZIP have a path ending with "/".
entry_.is_directory = base::EndsWith(in, u"/");
std::u16string normalized_path;
if (base::StartsWith(in, u"/")) {
normalized_path = u"ROOT";
entry_.is_unsafe = false;
}
for (;;) {
// Consume initial path separators.
const std::u16string_view::size_type i = in.find_first_not_of(u'/');
if (i == std::u16string_view::npos) {
break;
}
in.remove_prefix(i);
DCHECK(!in.empty());
// Isolate next path component.
const std::u16string_view part = in.substr(0, in.find_first_of(u'/'));
DCHECK(!part.empty());
in.remove_prefix(part.size());
if (!normalized_path.empty())
normalized_path += u'/';
if (part == u".") {
normalized_path += u"DOT";
entry_.is_unsafe = true;
continue;
}
if (part == u"..") {
normalized_path += u"UP";
entry_.is_unsafe = true;
continue;
}
// Windows has more restrictions than other systems when it comes to valid
// file paths. Replace Windows-invalid characters on all systems for
// consistency. In particular, this prevents a path component containing
// colon and backslash from being misinterpreted as an absolute path on
// Windows.
for (const char16_t c : part) {
normalized_path += IsValidFileNameCharacterOnWindows(c) ? c : 0xFFFD;
}
entry_.is_unsafe = false;
}
// If the entry is a directory, add the final path separator to the entry
// path.
if (entry_.is_directory && !normalized_path.empty()) {
normalized_path += u'/';
entry_.is_unsafe = false;
}
entry_.path = base::FilePath::FromUTF16Unsafe(normalized_path);
// By construction, we should always get a relative path.
DCHECK(!entry_.path.IsAbsolute()) << entry_.path;
}
void ZipReader::ReportProgress(ListenerCallback listener_callback,
uint64_t bytes) const {
delta_bytes_read_ += bytes;
const base::TimeTicks now = base::TimeTicks::Now();
if (next_progress_report_time_ > now)
return;
next_progress_report_time_ = now + progress_period_;
listener_callback.Run(delta_bytes_read_);
delta_bytes_read_ = 0;
}
bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
ListenerCallback listener_callback,
uint64_t num_bytes_to_extract) const {
DCHECK(zip_file_);
DCHECK_LT(0, next_index_);
DCHECK(ok_);
DCHECK(!reached_end_);
// Use password only for encrypted files. For non-encrypted files, no password
// is needed, and must be nullptr.
const char* const password =
entry_.is_encrypted ? password_.c_str() : nullptr;
if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)};
err != UNZ_OK) {
LOG(ERROR) << "Cannot open file " << Redact(entry_.path)
<< " from ZIP: " << err;
return false;
}
DCHECK(delegate);
if (!delegate->PrepareOutput())
return false;
uint64_t remaining_capacity = num_bytes_to_extract;
bool entire_file_extracted = false;
while (remaining_capacity > 0) {
char buf[internal::kZipBufSize];
const int num_bytes_read =
unzReadCurrentFile(zip_file_, buf, internal::kZipBufSize);
if (num_bytes_read == 0) {
entire_file_extracted = true;
break;
}
if (num_bytes_read < 0) {
LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
<< " from ZIP: " << UnzipError(num_bytes_read);
break;
}
if (listener_callback) {
ReportProgress(listener_callback, num_bytes_read);
}
DCHECK_LT(0, num_bytes_read);
CHECK_LE(num_bytes_read, internal::kZipBufSize);
uint64_t num_bytes_to_write = std::min<uint64_t>(
remaining_capacity, base::checked_cast<uint64_t>(num_bytes_read));
if (!delegate->WriteBytes(buf, num_bytes_to_write))
break;
if (remaining_capacity == base::checked_cast<uint64_t>(num_bytes_read)) {
// Ensures function returns true if the entire file has been read.
const int n = unzReadCurrentFile(zip_file_, buf, 1);
entire_file_extracted = (n == 0);
LOG_IF(ERROR, n < 0) << "Cannot read file " << Redact(entry_.path)
<< " from ZIP: " << UnzipError(n);
}
CHECK_GE(remaining_capacity, num_bytes_to_write);
remaining_capacity -= num_bytes_to_write;
}
if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) {
LOG(ERROR) << "Cannot extract file " << Redact(entry_.path)
<< " from ZIP: " << err;
entire_file_extracted = false;
}
if (entire_file_extracted) {
delegate->SetPosixFilePermissions(entry_.posix_mode);
if (entry_.last_modified != base::Time::UnixEpoch()) {
delegate->SetTimeModified(entry_.last_modified);
}
} else {
delegate->OnError();
}
if (listener_callback) {
listener_callback.Run(delta_bytes_read_);
delta_bytes_read_ = 0;
}
return entire_file_extracted;
}
bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
uint64_t num_bytes_to_extract) const {
return ExtractCurrentEntry(delegate, ListenerCallback(),
num_bytes_to_extract);
}
bool ZipReader::ExtractCurrentEntryWithListener(
WriterDelegate* delegate,
ListenerCallback listener_callback) const {
return ExtractCurrentEntry(delegate, listener_callback);
}
void ZipReader::ExtractCurrentEntryToFilePathAsync(
const base::FilePath& output_file_path,
SuccessCallback success_callback,
FailureCallback failure_callback,
ProgressCallback progress_callback) {
DCHECK(zip_file_);
DCHECK_LT(0, next_index_);
DCHECK(ok_);
DCHECK(!reached_end_);
// If this is a directory, just create it and return.
if (entry_.is_directory) {
if (base::CreateDirectory(output_file_path)) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(success_callback));
} else {
LOG(ERROR) << "Cannot create directory " << Redact(output_file_path);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(failure_callback));
}
return;
}
// Use password only for encrypted files. For non-encrypted files, no password
// is needed, and must be nullptr.
const char* const password =
entry_.is_encrypted ? password_.c_str() : nullptr;
if (const UnzipError err{unzOpenCurrentFilePassword(zip_file_, password)};
err != UNZ_OK) {
LOG(ERROR) << "Cannot open file " << Redact(entry_.path)
<< " from ZIP: " << err;
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(failure_callback));
return;
}
base::FilePath output_dir_path = output_file_path.DirName();
if (!base::CreateDirectory(output_dir_path)) {
LOG(ERROR) << "Cannot create directory " << Redact(output_dir_path);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(failure_callback));
return;
}
const int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
base::File output_file(output_file_path, flags);
if (!output_file.IsValid()) {
LOG(ERROR) << "Cannot create file " << Redact(output_file_path);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(failure_callback));
return;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
std::move(output_file), std::move(success_callback),
std::move(failure_callback), std::move(progress_callback),
0 /* initial offset */));
}
bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes,
std::string* output) const {
DCHECK(output);
DCHECK(zip_file_);
DCHECK_LT(0, next_index_);
DCHECK(ok_);
DCHECK(!reached_end_);
output->clear();
if (max_read_bytes == 0)
return true;
if (entry_.is_directory)
return true;
// The original_size is the best hint for the real size, so it saves doing
// reallocations for the common case when the uncompressed size is correct.
// However, we need to assume that the uncompressed size could be incorrect
// therefore this function needs to read as much data as possible.
output->reserve(base::checked_cast<size_t>(std::min<uint64_t>(
max_read_bytes, base::checked_cast<uint64_t>(entry_.original_size))));
StringWriterDelegate writer(output);
return ExtractCurrentEntry(&writer, max_read_bytes);
}
bool ZipReader::OpenInternal() {
DCHECK(zip_file_);
unz_global_info zip_info = {}; // Zero-clear.
if (const UnzipError err{unzGetGlobalInfo(zip_file_, &zip_info)};
err != UNZ_OK) {
LOG(ERROR) << "Cannot get ZIP info: " << err;
return false;
}
num_entries_ = zip_info.number_entry;
reached_end_ = (num_entries_ <= 0);
ok_ = true;
return true;
}
void ZipReader::Reset() {
zip_file_ = nullptr;
num_entries_ = 0;
next_index_ = 0;
reached_end_ = true;
ok_ = false;
delta_bytes_read_ = 0;
entry_ = {};
}
void ZipReader::ExtractChunk(base::File output_file,
SuccessCallback success_callback,
FailureCallback failure_callback,
ProgressCallback progress_callback,
int64_t offset) {
char buffer[internal::kZipBufSize];
const int num_bytes_read =
unzReadCurrentFile(zip_file_, buffer, internal::kZipBufSize);
if (num_bytes_read == 0) {
if (const UnzipError err{unzCloseCurrentFile(zip_file_)}; err != UNZ_OK) {
LOG(ERROR) << "Cannot extract file " << Redact(entry_.path)
<< " from ZIP: " << err;
std::move(failure_callback).Run();
return;
}
std::move(success_callback).Run();
return;
}
if (num_bytes_read < 0) {
LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
<< " from ZIP: " << UnzipError(num_bytes_read);
std::move(failure_callback).Run();
return;
}
if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) {
LOG(ERROR) << "Cannot write " << num_bytes_read
<< " bytes to file at offset " << offset;
std::move(failure_callback).Run();
return;
}
offset += num_bytes_read;
progress_callback.Run(offset);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(),
std::move(output_file), std::move(success_callback),
std::move(failure_callback), std::move(progress_callback),
offset));
}
// FileWriterDelegate ----------------------------------------------------------
FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) {
DCHECK(file_);
}
FileWriterDelegate::FileWriterDelegate(base::File owned_file)
: owned_file_(std::move(owned_file)) {
DCHECK_EQ(file_, &owned_file_);
}
FileWriterDelegate::~FileWriterDelegate() {}
bool FileWriterDelegate::PrepareOutput() {
DCHECK(file_);
if (!file_->IsValid()) {
LOG(ERROR) << "File is not valid";
return false;
}
const int64_t length = file_->GetLength();
if (length < 0) {
PLOG(ERROR) << "Cannot get length of file handle "
<< file_->GetPlatformFile();
return false;
}
// Just log a warning if the file is not empty.
// See crbug.com/1309879 and crbug.com/774762.
LOG_IF(WARNING, length > 0)
<< "File handle " << file_->GetPlatformFile()
<< " is not empty: Its length is " << length << " bytes";
return true;
}
bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
int bytes_written = file_->WriteAtCurrentPos(data, num_bytes);
if (bytes_written > 0)
file_length_ += bytes_written;
return bytes_written == num_bytes;
}
void FileWriterDelegate::SetTimeModified(const base::Time& time) {
file_->SetTimes(base::Time::Now(), time);
}
void FileWriterDelegate::SetPosixFilePermissions(int mode) {
#if defined(OS_POSIX)
zip::SetPosixFilePermissions(file_->GetPlatformFile(), mode);
#endif
}
void FileWriterDelegate::OnError() {
file_length_ = 0;
file_->SetLength(0);
}
// FilePathWriterDelegate ------------------------------------------------------
FilePathWriterDelegate::FilePathWriterDelegate(base::FilePath output_file_path)
: FileWriterDelegate(base::File()),
output_file_path_(std::move(output_file_path)) {}
FilePathWriterDelegate::~FilePathWriterDelegate() {}
bool FilePathWriterDelegate::PrepareOutput() {
// We can't rely on parent directory entries being specified in the
// zip, so we make sure they are created.
if (const base::FilePath dir = output_file_path_.DirName();
!base::CreateDirectory(dir)) {
PLOG(ERROR) << "Cannot create directory " << Redact(dir);
return false;
}
owned_file_.Initialize(output_file_path_,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
if (!owned_file_.IsValid()) {
PLOG(ERROR) << "Cannot create file " << Redact(output_file_path_) << ": "
<< base::File::ErrorToString(owned_file_.error_details());
return false;
}
const int64_t length = owned_file_.GetLength();
if (length < 0) {
PLOG(ERROR) << "Cannot get length of file " << Redact(output_file_path_);
return false;
}
if (length > 0) {
LOG(ERROR) << "File " << Redact(output_file_path_)
<< " is not empty: Its length is " << length << " bytes";
return false;
}
return true;
}
void FilePathWriterDelegate::OnError() {
FileWriterDelegate::OnError();
owned_file_.Close();
if (!base::DeleteFile(output_file_path_)) {
LOG(ERROR) << "Cannot delete partially extracted file "
<< Redact(output_file_path_);
}
}
} // namespace zip

405
deps/zlib/google/zip_reader.h vendored Normal file
View File

@ -0,0 +1,405 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
#define THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <memory>
#include <string>
#include <string_view>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#if defined(USE_SYSTEM_MINIZIP)
#include <minizip/unzip.h>
#else
#include "third_party/zlib/contrib/minizip/unzip.h"
#endif
namespace zip {
// A delegate interface used to stream out an entry; see
// ZipReader::ExtractCurrentEntry.
class WriterDelegate {
public:
virtual ~WriterDelegate() {}
// Invoked once before any data is streamed out to pave the way (e.g., to open
// the output file). Return false on failure to cancel extraction.
virtual bool PrepareOutput() { return true; }
// Invoked to write the next chunk of data. Return false on failure to cancel
// extraction.
virtual bool WriteBytes(const char* data, int num_bytes) { return true; }
// Sets the last-modified time of the data.
virtual void SetTimeModified(const base::Time& time) {}
// Called with the POSIX file permissions of the data; POSIX implementations
// may apply some of the permissions (for example, the executable bit) to the
// output file.
virtual void SetPosixFilePermissions(int mode) {}
// Called if an error occurred while extracting the file. The WriterDelegate
// can then remove and clean up the partially extracted data.
virtual void OnError() {}
};
// This class is used for reading ZIP archives. A typical use case of this class
// is to scan entries in a ZIP archive and extract them. The code will look
// like:
//
// ZipReader reader;
// if (!reader.Open(zip_path)) {
// // Cannot open
// return;
// }
//
// while (const ZipReader::entry* entry = reader.Next()) {
// auto writer = CreateFilePathWriterDelegate(extract_dir, entry->path);
// if (!reader.ExtractCurrentEntry(writer)) {
// // Cannot extract
// return;
// }
// }
//
// if (!reader.ok()) {
// // Error while enumerating entries
// return;
// }
//
class ZipReader {
public:
// A callback that is called when the operation is successful.
using SuccessCallback = base::OnceClosure;
// A callback that is called when the operation fails.
using FailureCallback = base::OnceClosure;
// A callback that is called periodically during the operation with the number
// of bytes that have been processed so far.
using ProgressCallback = base::RepeatingCallback<void(int64_t)>;
// A callback that is called periodically during the operation with the number
// of bytes that have been processed since the previous call (i.e. delta).
using ListenerCallback = base::RepeatingCallback<void(uint64_t)>;
// Information of an entry (file or directory) in a ZIP archive.
struct Entry {
// Path of this entry, in its original encoding as it is stored in the ZIP
// archive. The encoding is not specified here. It might or might not be
// UTF-8, and the caller needs to use other means to determine the encoding
// if it wants to interpret this path correctly.
std::string path_in_original_encoding;
// Path of the entry, converted to Unicode. This path is relative (eg
// "foo/bar.txt"). Absolute paths (eg "/foo/bar.txt") or paths containing
// ".." or "." components (eg "../foo/bar.txt") are converted to safe
// relative paths. Eg:
// (In ZIP) -> (Entry.path)
// /foo/bar -> ROOT/foo/bar
// ../a -> UP/a
// ./a -> DOT/a
base::FilePath path;
// Size of the original uncompressed file, or 0 if the entry is a directory.
// This value should not be trusted, because it is stored as metadata in the
// ZIP archive and can be different from the real uncompressed size.
int64_t original_size;
// Last modified time. If the timestamp stored in the ZIP archive is not
// valid, the Unix epoch will be returned.
//
// The timestamp stored in the ZIP archive uses the MS-DOS date and time
// format.
//
// http://msdn.microsoft.com/en-us/library/ms724247(v=vs.85).aspx
//
// As such the following limitations apply:
// * Only years from 1980 to 2107 can be represented.
// * The timestamp has a 2-second resolution.
// * There is no timezone information, so the time is interpreted as UTC.
base::Time last_modified;
// True if the entry is a directory.
// False if the entry is a file.
bool is_directory = false;
// True if the entry path cannot be converted to a safe relative path. This
// happens if a file entry (not a directory) has a filename "." or "..".
bool is_unsafe = false;
// True if the file content is encrypted.
bool is_encrypted = false;
// True if the encryption scheme is AES.
bool uses_aes_encryption = false;
// Entry POSIX permissions (POSIX systems only).
int posix_mode;
};
ZipReader();
ZipReader(const ZipReader&) = delete;
ZipReader& operator=(const ZipReader&) = delete;
~ZipReader();
// Opens the ZIP archive specified by |zip_path|. Returns true on
// success.
bool Open(const base::FilePath& zip_path);
// Opens the ZIP archive referred to by the platform file |zip_fd|, without
// taking ownership of |zip_fd|. Returns true on success.
bool OpenFromPlatformFile(base::PlatformFile zip_fd);
// Opens the zip data stored in |data|. This class uses a weak reference to
// the given sring while extracting files, i.e. the caller should keep the
// string until it finishes extracting files.
bool OpenFromString(const std::string& data);
// Closes the currently opened ZIP archive. This function is called in the
// destructor of the class, so you usually don't need to call this.
void Close();
// Sets the encoding of entry paths in the ZIP archive.
// By default, paths are assumed to be in UTF-8.
void SetEncoding(std::string encoding) { encoding_ = std::move(encoding); }
// Sets the decryption password that will be used to decrypt encrypted file in
// the ZIP archive.
void SetPassword(std::string password) { password_ = std::move(password); }
// Gets the next entry. Returns null if there is no more entry, or if an error
// occurred while scanning entries. The returned Entry is owned by this
// ZipReader, and is valid until Next() is called again or until this
// ZipReader is closed.
//
// This function should be called before operations over the current entry
// like ExtractCurrentEntryToFile().
//
// while (const ZipReader::Entry* entry = reader.Next()) {
// // Do something with the current entry here.
// ...
// }
//
// // Finished scanning entries.
// // Check if the scanning stopped because of an error.
// if (!reader.ok()) {
// // There was an error.
// ...
// }
const Entry* Next();
// Returns true if the enumeration of entries was successful, or false if it
// stopped because of an error.
bool ok() const { return ok_; }
// Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|,
// starting from the beginning of the entry.
//
// Returns true if the entire file was extracted without error.
//
// Precondition: Next() returned a non-null Entry.
bool ExtractCurrentEntry(WriterDelegate* delegate,
uint64_t num_bytes_to_extract =
std::numeric_limits<uint64_t>::max()) const;
// Extracts the current entry to |delegate|, starting from the beginning
// of the entry, calling |listener_callback| regularly with the number of
// bytes extracted.
//
// Returns true if the entire file was extracted without error.
//
// Precondition: Next() returned a non-null Entry.
bool ExtractCurrentEntryWithListener(
WriterDelegate* delegate,
ListenerCallback listener_callback) const;
// Asynchronously extracts the current entry to the given output file path. If
// the current entry is a directory it just creates the directory
// synchronously instead.
//
// |success_callback| will be called on success and |failure_callback| will be
// called on failure. |progress_callback| will be called at least once.
// Callbacks will be posted to the current MessageLoop in-order.
//
// Precondition: Next() returned a non-null Entry.
void ExtractCurrentEntryToFilePathAsync(
const base::FilePath& output_file_path,
SuccessCallback success_callback,
FailureCallback failure_callback,
ProgressCallback progress_callback);
// Extracts the current entry into memory. If the current entry is a
// directory, |*output| is set to the empty string. If the current entry is a
// file, |*output| is filled with its contents.
//
// The value in |Entry::original_size| cannot be trusted, so the real size of
// the uncompressed contents can be different. |max_read_bytes| limits the
// amount of memory used to carry the entry.
//
// Returns true if the entire content is read without error. If the content is
// bigger than |max_read_bytes|, this function returns false and |*output| is
// filled with |max_read_bytes| of data. If an error occurs, this function
// returns false and |*output| contains the content extracted so far, which
// might be garbage data.
//
// Precondition: Next() returned a non-null Entry.
bool ExtractCurrentEntryToString(uint64_t max_read_bytes,
std::string* output) const;
bool ExtractCurrentEntryToString(std::string* output) const {
return ExtractCurrentEntryToString(
base::checked_cast<uint64_t>(output->max_size()), output);
}
// Returns the number of entries in the ZIP archive.
//
// Precondition: one of the Open() methods returned true.
int num_entries() const { return num_entries_; }
private:
// Common code used both in Open and OpenFromFd.
bool OpenInternal();
// Resets the internal state.
void Reset();
// Opens the current entry in the ZIP archive. On success, returns true and
// updates the current entry state |entry_|.
//
// Note that there is no matching CloseEntry(). The current entry state is
// reset automatically as needed.
bool OpenEntry();
// Normalizes the given path passed as UTF-16 string piece. Sets entry_.path,
// entry_.is_directory and entry_.is_unsafe.
void Normalize(std::u16string_view in);
// Runs the ListenerCallback at a throttled rate.
void ReportProgress(ListenerCallback listener_callback, uint64_t bytes) const;
// Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|,
// starting from the beginning of the entry calling |listener_callback| if
// its supplied.
//
// Returns true if the entire file was extracted without error.
//
// Precondition: Next() returned a non-null Entry.
bool ExtractCurrentEntry(WriterDelegate* delegate,
ListenerCallback listener_callback,
uint64_t num_bytes_to_extract =
std::numeric_limits<uint64_t>::max()) const;
// Extracts a chunk of the file to the target. Will post a task for the next
// chunk and success/failure/progress callbacks as necessary.
void ExtractChunk(base::File target_file,
SuccessCallback success_callback,
FailureCallback failure_callback,
ProgressCallback progress_callback,
const int64_t offset);
std::string encoding_;
std::string password_;
unzFile zip_file_;
int num_entries_;
int next_index_;
bool reached_end_;
bool ok_;
Entry entry_;
// Next time to report progress.
mutable base::TimeTicks next_progress_report_time_ = base::TimeTicks::Now();
// Progress time delta.
// TODO(crbug.com/953256) Add this as parameter to the unzip options.
base::TimeDelta progress_period_ = base::Milliseconds(1000);
// Number of bytes read since last progress report callback executed.
mutable uint64_t delta_bytes_read_ = 0;
base::WeakPtrFactory<ZipReader> weak_ptr_factory_{this};
};
// A writer delegate that writes to a given File. It is recommended that this
// file be initially empty.
class FileWriterDelegate : public WriterDelegate {
public:
// Constructs a FileWriterDelegate that manipulates |file|. The delegate will
// not own |file|, therefore the caller must guarantee |file| will outlive the
// delegate.
explicit FileWriterDelegate(base::File* file);
// Constructs a FileWriterDelegate that takes ownership of |file|.
explicit FileWriterDelegate(base::File owned_file);
FileWriterDelegate(const FileWriterDelegate&) = delete;
FileWriterDelegate& operator=(const FileWriterDelegate&) = delete;
~FileWriterDelegate() override;
// Returns true if the file handle passed to the constructor is valid.
bool PrepareOutput() override;
// Writes |num_bytes| bytes of |data| to the file, returning false on error or
// if not all bytes could be written.
bool WriteBytes(const char* data, int num_bytes) override;
// Sets the last-modified time of the data.
void SetTimeModified(const base::Time& time) override;
// On POSIX systems, sets the file to be executable if the source file was
// executable.
void SetPosixFilePermissions(int mode) override;
// Empties the file to avoid leaving garbage data in it.
void OnError() override;
// Gets the number of bytes written into the file.
int64_t file_length() { return file_length_; }
protected:
// The delegate can optionally own the file it modifies, in which case
// owned_file_ is set and file_ is an alias for owned_file_.
base::File owned_file_;
// The file the delegate modifies.
base::File* const file_ = &owned_file_;
int64_t file_length_ = 0;
};
// A writer delegate that creates and writes a file at a given path. This does
// not overwrite any existing file.
class FilePathWriterDelegate : public FileWriterDelegate {
public:
explicit FilePathWriterDelegate(base::FilePath output_file_path);
FilePathWriterDelegate(const FilePathWriterDelegate&) = delete;
FilePathWriterDelegate& operator=(const FilePathWriterDelegate&) = delete;
~FilePathWriterDelegate() override;
// Creates the output file and any necessary intermediate directories. Does
// not overwrite any existing file, and returns false if the output file
// cannot be created because another file conflicts with it.
bool PrepareOutput() override;
// Deletes the output file.
void OnError() override;
private:
const base::FilePath output_file_path_;
};
} // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_

936
deps/zlib/google/zip_reader_unittest.cc vendored Normal file
View File

@ -0,0 +1,936 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip_reader.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <iterator>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "base/check.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/hash/md5.h"
#include "base/i18n/time_formatting.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
#include "third_party/zlib/google/zip_internal.h"
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Return;
using ::testing::SizeIs;
namespace {
const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
class FileWrapper {
public:
typedef enum { READ_ONLY, READ_WRITE } AccessMode;
FileWrapper(const base::FilePath& path, AccessMode mode) {
int flags = base::File::FLAG_READ;
if (mode == READ_ONLY)
flags |= base::File::FLAG_OPEN;
else
flags |= base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS;
file_.Initialize(path, flags);
}
~FileWrapper() {}
base::PlatformFile platform_file() { return file_.GetPlatformFile(); }
base::File* file() { return &file_; }
private:
base::File file_;
};
// A mock that provides methods that can be used as callbacks in asynchronous
// unzip functions. Tracks the number of calls and number of bytes reported.
// Assumes that progress callbacks will be executed in-order.
class MockUnzipListener final {
public:
MockUnzipListener()
: success_calls_(0),
failure_calls_(0),
progress_calls_(0),
current_progress_(0) {}
// Success callback for async functions.
void OnUnzipSuccess() { success_calls_++; }
// Failure callback for async functions.
void OnUnzipFailure() { failure_calls_++; }
// Progress callback for async functions.
void OnUnzipProgress(int64_t progress) {
DCHECK(progress > current_progress_);
progress_calls_++;
current_progress_ = progress;
}
int success_calls() { return success_calls_; }
int failure_calls() { return failure_calls_; }
int progress_calls() { return progress_calls_; }
int current_progress() { return current_progress_; }
base::WeakPtr<MockUnzipListener> AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
int success_calls_;
int failure_calls_;
int progress_calls_;
int64_t current_progress_;
base::WeakPtrFactory<MockUnzipListener> weak_ptr_factory_{this};
};
class MockWriterDelegate : public zip::WriterDelegate {
public:
MOCK_METHOD0(PrepareOutput, bool());
MOCK_METHOD2(WriteBytes, bool(const char*, int));
MOCK_METHOD1(SetTimeModified, void(const base::Time&));
MOCK_METHOD1(SetPosixFilePermissions, void(int));
MOCK_METHOD0(OnError, void());
};
bool ExtractCurrentEntryToFilePath(zip::ZipReader* reader,
base::FilePath path) {
zip::FilePathWriterDelegate writer(path);
return reader->ExtractCurrentEntry(&writer);
}
const zip::ZipReader::Entry* LocateAndOpenEntry(
zip::ZipReader* const reader,
const base::FilePath& path_in_zip) {
DCHECK(reader);
EXPECT_TRUE(reader->ok());
// The underlying library can do O(1) access, but ZipReader does not expose
// that. O(N) access is acceptable for these tests.
while (const zip::ZipReader::Entry* const entry = reader->Next()) {
EXPECT_TRUE(reader->ok());
if (entry->path == path_in_zip)
return entry;
}
EXPECT_TRUE(reader->ok());
return nullptr;
}
using Paths = std::vector<base::FilePath>;
} // namespace
namespace zip {
// Make the test a PlatformTest to setup autorelease pools properly on Mac.
class ZipReaderTest : public PlatformTest {
protected:
void SetUp() override {
PlatformTest::SetUp();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
test_dir_ = temp_dir_.GetPath();
}
static base::FilePath GetTestDataDirectory() {
base::FilePath path;
CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path));
return path.AppendASCII("third_party")
.AppendASCII("zlib")
.AppendASCII("google")
.AppendASCII("test")
.AppendASCII("data");
}
static Paths GetPaths(const base::FilePath& zip_path,
std::string_view encoding = {}) {
Paths paths;
if (ZipReader reader; reader.Open(zip_path)) {
if (!encoding.empty())
reader.SetEncoding(std::string(encoding));
while (const ZipReader::Entry* const entry = reader.Next()) {
EXPECT_TRUE(reader.ok());
paths.push_back(entry->path);
}
EXPECT_TRUE(reader.ok());
}
return paths;
}
// The path to temporary directory used to contain the test operations.
base::FilePath test_dir_;
// The path to the test data directory where test.zip etc. are located.
const base::FilePath data_dir_ = GetTestDataDirectory();
// The path to test.zip in the test data directory.
const base::FilePath test_zip_file_ = data_dir_.AppendASCII("test.zip");
const Paths test_zip_contents_ = {
base::FilePath(FILE_PATH_LITERAL("foo/")),
base::FilePath(FILE_PATH_LITERAL("foo/bar/")),
base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt")),
base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt")),
base::FilePath(FILE_PATH_LITERAL("foo/bar.txt")),
base::FilePath(FILE_PATH_LITERAL("foo.txt")),
base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden")),
};
base::ScopedTempDir temp_dir_;
base::test::TaskEnvironment task_environment_;
};
TEST_F(ZipReaderTest, Open_ValidZipFile) {
ZipReader reader;
EXPECT_TRUE(reader.Open(test_zip_file_));
EXPECT_TRUE(reader.ok());
}
TEST_F(ZipReaderTest, Open_ValidZipPlatformFile) {
ZipReader reader;
EXPECT_FALSE(reader.ok());
FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
EXPECT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
EXPECT_TRUE(reader.ok());
}
TEST_F(ZipReaderTest, Open_NonExistentFile) {
ZipReader reader;
EXPECT_FALSE(reader.ok());
EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("nonexistent.zip")));
EXPECT_FALSE(reader.ok());
}
TEST_F(ZipReaderTest, Open_ExistentButNonZipFile) {
ZipReader reader;
EXPECT_FALSE(reader.ok());
EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("create_test_zip.sh")));
EXPECT_FALSE(reader.ok());
}
TEST_F(ZipReaderTest, Open_EmptyFile) {
ZipReader reader;
EXPECT_FALSE(reader.ok());
EXPECT_TRUE(reader.Open(data_dir_.AppendASCII("empty.zip")));
EXPECT_TRUE(reader.ok());
EXPECT_EQ(0, reader.num_entries());
EXPECT_EQ(nullptr, reader.Next());
}
// Iterate through the contents in the test ZIP archive, and compare that the
// contents collected from the ZipReader matches the expected contents.
TEST_F(ZipReaderTest, Iteration) {
Paths actual_contents;
ZipReader reader;
EXPECT_FALSE(reader.ok());
EXPECT_TRUE(reader.Open(test_zip_file_));
EXPECT_TRUE(reader.ok());
while (const ZipReader::Entry* const entry = reader.Next()) {
EXPECT_TRUE(reader.ok());
actual_contents.push_back(entry->path);
}
EXPECT_TRUE(reader.ok());
EXPECT_FALSE(reader.Next()); // Shouldn't go further.
EXPECT_TRUE(reader.ok());
EXPECT_THAT(actual_contents, SizeIs(reader.num_entries()));
EXPECT_THAT(actual_contents, ElementsAreArray(test_zip_contents_));
}
// Open the test ZIP archive from a file descriptor, iterate through its
// contents, and compare that they match the expected contents.
TEST_F(ZipReaderTest, PlatformFileIteration) {
Paths actual_contents;
ZipReader reader;
FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
EXPECT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
EXPECT_TRUE(reader.ok());
while (const ZipReader::Entry* const entry = reader.Next()) {
EXPECT_TRUE(reader.ok());
actual_contents.push_back(entry->path);
}
EXPECT_TRUE(reader.ok());
EXPECT_FALSE(reader.Next()); // Shouldn't go further.
EXPECT_TRUE(reader.ok());
EXPECT_THAT(actual_contents, SizeIs(reader.num_entries()));
EXPECT_THAT(actual_contents, ElementsAreArray(test_zip_contents_));
}
TEST_F(ZipReaderTest, RegularFile) {
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
EXPECT_EQ(13527, entry->original_size);
EXPECT_EQ("2009-05-29 06:22:20.000",
base::UnlocalizedTimeFormatWithPattern(entry->last_modified,
"y-MM-dd HH:mm:ss.SSS",
icu::TimeZone::getGMT()));
EXPECT_FALSE(entry->is_unsafe);
EXPECT_FALSE(entry->is_directory);
}
TEST_F(ZipReaderTest, DotDotFile) {
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil.zip")));
base::FilePath target_path(FILE_PATH_LITERAL(
"UP/levilevilevilevilevilevilevilevilevilevilevilevil"));
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
EXPECT_FALSE(entry->is_unsafe);
EXPECT_FALSE(entry->is_directory);
}
TEST_F(ZipReaderTest, InvalidUTF8File) {
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil_via_invalid_utf8.zip")));
base::FilePath target_path = base::FilePath::FromUTF8Unsafe(".<2E>.<2E>evil.txt");
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
EXPECT_FALSE(entry->is_unsafe);
EXPECT_FALSE(entry->is_directory);
}
// By default, file paths in ZIPs are interpreted as UTF-8. But in this test,
// the ZIP archive contains file paths that are actually encoded in Shift JIS.
// The SJIS-encoded paths are thus wrongly interpreted as UTF-8, resulting in
// garbled paths. Invalid UTF-8 sequences are safely converted to the
// replacement character <20>.
TEST_F(ZipReaderTest, EncodingSjisAsUtf8) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip")),
ElementsAre(
base::FilePath::FromUTF8Unsafe("<EFBFBD>V<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>t<EFBFBD>H<EFBFBD><EFBFBD><EFBFBD>_/SJIS_835C_<43><5F>.txt"),
base::FilePath::FromUTF8Unsafe(
"<EFBFBD>V<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>t<EFBFBD>H<EFBFBD><EFBFBD><EFBFBD>_/<2F>V<EFBFBD><56><EFBFBD><EFBFBD><EFBFBD>e<EFBFBD>L<EFBFBD>X<EFBFBD>g <20>h<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>g.txt")));
}
// In this test, SJIS-encoded paths are interpreted as Code Page 1252. This
// results in garbled paths. Note the presence of C1 control codes U+0090 and
// U+0081 in the garbled paths.
TEST_F(ZipReaderTest, EncodingSjisAs1252) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "windows-1252"),
ElementsAre(base::FilePath::FromUTF8Unsafe(
"\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/SJIS_835C_ƒ<5F>.txt"),
base::FilePath::FromUTF8Unsafe(
"\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/\u0090V‚µ‚¢ƒeƒLƒXƒg "
"ƒhƒLƒ…ƒ\u0081ƒ“ƒg.txt")));
}
// In this test, SJIS-encoded paths are interpreted as Code Page 866. This
// results in garbled paths.
TEST_F(ZipReaderTest, EncodingSjisAsIbm866) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "IBM866"),
ElementsAre(
base::FilePath::FromUTF8Unsafe("РVВ╡ВвГtГHГЛГ_/SJIS_835C_Г<5F>.txt"),
base::FilePath::FromUTF8Unsafe(
"РVВ╡ВвГtГHГЛГ_/РVВ╡ВвГeГLГXГg ГhГLГЕГБГУГg.txt")));
}
// Tests that SJIS-encoded paths are correctly converted to Unicode.
TEST_F(ZipReaderTest, EncodingSjis) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "Shift_JIS"),
ElementsAre(
base::FilePath::FromUTF8Unsafe("新しいフォルダ/SJIS_835C_ソ.txt"),
base::FilePath::FromUTF8Unsafe(
"新しいフォルダ/新しいテキスト ドキュメント.txt")));
}
TEST_F(ZipReaderTest, AbsoluteFile) {
ZipReader reader;
ASSERT_TRUE(
reader.Open(data_dir_.AppendASCII("evil_via_absolute_file_name.zip")));
base::FilePath target_path(FILE_PATH_LITERAL("ROOT/evil.txt"));
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
EXPECT_FALSE(entry->is_unsafe);
EXPECT_FALSE(entry->is_directory);
}
TEST_F(ZipReaderTest, Directory) {
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/"));
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
// The directory size should be zero.
EXPECT_EQ(0, entry->original_size);
EXPECT_EQ("2009-05-31 15:49:52.000",
base::UnlocalizedTimeFormatWithPattern(entry->last_modified,
"y-MM-dd HH:mm:ss.SSS",
icu::TimeZone::getGMT()));
EXPECT_FALSE(entry->is_unsafe);
EXPECT_TRUE(entry->is_directory);
}
TEST_F(ZipReaderTest, EncryptedFile_WrongPassword) {
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip")));
reader.SetPassword("wrong password");
{
const ZipReader::Entry* entry = reader.Next();
ASSERT_TRUE(entry);
EXPECT_EQ(base::FilePath::FromASCII("ClearText.txt"), entry->path);
EXPECT_FALSE(entry->is_directory);
EXPECT_FALSE(entry->is_encrypted);
std::string contents = "dummy";
EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents));
EXPECT_EQ("This is not encrypted.\n", contents);
}
for (const std::string_view path : {
"Encrypted AES-128.txt",
"Encrypted AES-192.txt",
"Encrypted AES-256.txt",
"Encrypted ZipCrypto.txt",
}) {
const ZipReader::Entry* entry = reader.Next();
ASSERT_TRUE(entry);
EXPECT_EQ(base::FilePath::FromASCII(path), entry->path);
EXPECT_FALSE(entry->is_directory);
EXPECT_TRUE(entry->is_encrypted);
std::string contents = "dummy";
EXPECT_FALSE(reader.ExtractCurrentEntryToString(&contents));
}
EXPECT_FALSE(reader.Next());
EXPECT_TRUE(reader.ok());
}
TEST_F(ZipReaderTest, EncryptedFile_RightPassword) {
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip")));
reader.SetPassword("password");
{
const ZipReader::Entry* entry = reader.Next();
ASSERT_TRUE(entry);
EXPECT_EQ(base::FilePath::FromASCII("ClearText.txt"), entry->path);
EXPECT_FALSE(entry->is_directory);
EXPECT_FALSE(entry->is_encrypted);
std::string contents = "dummy";
EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents));
EXPECT_EQ("This is not encrypted.\n", contents);
}
// TODO(crbug.com/1296838) Support AES encryption.
for (const std::string_view path : {
"Encrypted AES-128.txt",
"Encrypted AES-192.txt",
"Encrypted AES-256.txt",
}) {
const ZipReader::Entry* entry = reader.Next();
ASSERT_TRUE(entry);
EXPECT_EQ(base::FilePath::FromASCII(path), entry->path);
EXPECT_FALSE(entry->is_directory);
EXPECT_TRUE(entry->is_encrypted);
std::string contents = "dummy";
EXPECT_FALSE(reader.ExtractCurrentEntryToString(&contents));
EXPECT_EQ("", contents);
}
{
const ZipReader::Entry* entry = reader.Next();
ASSERT_TRUE(entry);
EXPECT_EQ(base::FilePath::FromASCII("Encrypted ZipCrypto.txt"),
entry->path);
EXPECT_FALSE(entry->is_directory);
EXPECT_TRUE(entry->is_encrypted);
std::string contents = "dummy";
EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents));
EXPECT_EQ("This is encrypted with ZipCrypto.\n", contents);
}
EXPECT_FALSE(reader.Next());
EXPECT_TRUE(reader.ok());
}
// Verifies that the ZipReader class can extract a file from a zip archive
// stored in memory. This test opens a zip archive in a std::string object,
// extracts its content, and verifies the content is the same as the expected
// text.
TEST_F(ZipReaderTest, OpenFromString) {
// A zip archive consisting of one file "test.txt", which is a 16-byte text
// file that contains "This is a test.\n".
const char kTestData[] =
"\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\xa4\x66\x24\x41\x13\xe8"
"\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00\x1c\x00\x74\x65"
"\x73\x74\x2e\x74\x78\x74\x55\x54\x09\x00\x03\x34\x89\x45\x50\x34"
"\x89\x45\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13"
"\x00\x00\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74"
"\x2e\x0a\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\xa4\x66"
"\x24\x41\x13\xe8\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00"
"\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa4\x81\x00\x00\x00\x00"
"\x74\x65\x73\x74\x2e\x74\x78\x74\x55\x54\x05\x00\x03\x34\x89\x45"
"\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13\x00\x00"
"\x50\x4b\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4e\x00\x00\x00"
"\x52\x00\x00\x00\x00\x00";
std::string data(kTestData, std::size(kTestData));
ZipReader reader;
ASSERT_TRUE(reader.OpenFromString(data));
base::FilePath target_path(FILE_PATH_LITERAL("test.txt"));
ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
ASSERT_TRUE(ExtractCurrentEntryToFilePath(&reader,
test_dir_.AppendASCII("test.txt")));
std::string actual;
ASSERT_TRUE(
base::ReadFileToString(test_dir_.AppendASCII("test.txt"), &actual));
EXPECT_EQ(std::string("This is a test.\n"), actual);
}
// Verifies that the asynchronous extraction to a file works.
TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) {
MockUnzipListener listener;
ZipReader reader;
base::FilePath target_file = test_dir_.AppendASCII("quux.txt");
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
reader.ExtractCurrentEntryToFilePathAsync(
target_file,
base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()),
base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()),
base::BindRepeating(&MockUnzipListener::OnUnzipProgress,
listener.AsWeakPtr()));
EXPECT_EQ(0, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_EQ(0, listener.progress_calls());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_LE(1, listener.progress_calls());
std::string output;
ASSERT_TRUE(
base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), &output));
const std::string md5 = base::MD5String(output);
EXPECT_EQ(kQuuxExpectedMD5, md5);
std::optional<int64_t> file_size = base::GetFileSize(target_file);
ASSERT_TRUE(file_size.has_value());
EXPECT_EQ(file_size.value(), listener.current_progress());
}
TEST_F(ZipReaderTest, ExtractToFileAsync_Encrypted_NoPassword) {
MockUnzipListener listener;
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip")));
ASSERT_TRUE(LocateAndOpenEntry(
&reader, base::FilePath::FromASCII("Encrypted ZipCrypto.txt")));
const base::FilePath target_path = test_dir_.AppendASCII("extracted");
reader.ExtractCurrentEntryToFilePathAsync(
target_path,
base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()),
base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()),
base::BindRepeating(&MockUnzipListener::OnUnzipProgress,
listener.AsWeakPtr()));
EXPECT_EQ(0, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_EQ(0, listener.progress_calls());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, listener.success_calls());
EXPECT_EQ(1, listener.failure_calls());
EXPECT_LE(1, listener.progress_calls());
// The extracted file contains rubbish data.
// We probably shouldn't even look at it.
std::string contents;
ASSERT_TRUE(base::ReadFileToString(target_path, &contents));
EXPECT_NE("", contents);
EXPECT_EQ(contents.size(), listener.current_progress());
}
TEST_F(ZipReaderTest, ExtractToFileAsync_Encrypted_RightPassword) {
MockUnzipListener listener;
ZipReader reader;
reader.SetPassword("password");
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip")));
ASSERT_TRUE(LocateAndOpenEntry(
&reader, base::FilePath::FromASCII("Encrypted ZipCrypto.txt")));
const base::FilePath target_path = test_dir_.AppendASCII("extracted");
reader.ExtractCurrentEntryToFilePathAsync(
target_path,
base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()),
base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()),
base::BindRepeating(&MockUnzipListener::OnUnzipProgress,
listener.AsWeakPtr()));
EXPECT_EQ(0, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_EQ(0, listener.progress_calls());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_LE(1, listener.progress_calls());
std::string contents;
ASSERT_TRUE(base::ReadFileToString(target_path, &contents));
EXPECT_EQ("This is encrypted with ZipCrypto.\n", contents);
EXPECT_EQ(contents.size(), listener.current_progress());
}
TEST_F(ZipReaderTest, ExtractToFileAsync_WrongCrc) {
MockUnzipListener listener;
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Wrong CRC.zip")));
ASSERT_TRUE(
LocateAndOpenEntry(&reader, base::FilePath::FromASCII("Corrupted.txt")));
const base::FilePath target_path = test_dir_.AppendASCII("extracted");
reader.ExtractCurrentEntryToFilePathAsync(
target_path,
base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()),
base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()),
base::BindRepeating(&MockUnzipListener::OnUnzipProgress,
listener.AsWeakPtr()));
EXPECT_EQ(0, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_EQ(0, listener.progress_calls());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, listener.success_calls());
EXPECT_EQ(1, listener.failure_calls());
EXPECT_LE(1, listener.progress_calls());
std::string contents;
ASSERT_TRUE(base::ReadFileToString(target_path, &contents));
EXPECT_EQ("This file has been changed after its CRC was computed.\n",
contents);
EXPECT_EQ(contents.size(), listener.current_progress());
}
// Verifies that the asynchronous extraction to a file works.
TEST_F(ZipReaderTest, ExtractToFileAsync_Directory) {
MockUnzipListener listener;
ZipReader reader;
base::FilePath target_file = test_dir_.AppendASCII("foo");
base::FilePath target_path(FILE_PATH_LITERAL("foo/"));
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
reader.ExtractCurrentEntryToFilePathAsync(
target_file,
base::BindOnce(&MockUnzipListener::OnUnzipSuccess, listener.AsWeakPtr()),
base::BindOnce(&MockUnzipListener::OnUnzipFailure, listener.AsWeakPtr()),
base::BindRepeating(&MockUnzipListener::OnUnzipProgress,
listener.AsWeakPtr()));
EXPECT_EQ(0, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_EQ(0, listener.progress_calls());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, listener.success_calls());
EXPECT_EQ(0, listener.failure_calls());
EXPECT_GE(0, listener.progress_calls());
ASSERT_TRUE(base::DirectoryExists(target_file));
}
TEST_F(ZipReaderTest, ExtractCurrentEntryToString) {
// test_mismatch_size.zip contains files with names from 0.txt to 7.txt with
// sizes from 0 to 7 bytes respectively, being the contents of each file a
// substring of "0123456" starting at '0'.
base::FilePath test_zip_file =
data_dir_.AppendASCII("test_mismatch_size.zip");
ZipReader reader;
std::string contents;
ASSERT_TRUE(reader.Open(test_zip_file));
for (size_t i = 0; i < 8; i++) {
SCOPED_TRACE(base::StringPrintf("Processing %d.txt", static_cast<int>(i)));
base::FilePath file_name = base::FilePath::FromUTF8Unsafe(
base::StringPrintf("%d.txt", static_cast<int>(i)));
ASSERT_TRUE(LocateAndOpenEntry(&reader, file_name));
if (i > 1) {
// Off by one byte read limit: must fail.
EXPECT_FALSE(reader.ExtractCurrentEntryToString(i - 1, &contents));
}
if (i > 0) {
// Exact byte read limit: must pass.
EXPECT_TRUE(reader.ExtractCurrentEntryToString(i, &contents));
EXPECT_EQ(std::string(std::string_view("0123456", i)), contents);
}
// More than necessary byte read limit: must pass.
EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents));
EXPECT_EQ(std::string(std::string_view("0123456", i)), contents);
}
reader.Close();
}
TEST_F(ZipReaderTest, ExtractPartOfCurrentEntry) {
// test_mismatch_size.zip contains files with names from 0.txt to 7.txt with
// sizes from 0 to 7 bytes respectively, being the contents of each file a
// substring of "0123456" starting at '0'.
base::FilePath test_zip_file =
data_dir_.AppendASCII("test_mismatch_size.zip");
ZipReader reader;
std::string contents;
ASSERT_TRUE(reader.Open(test_zip_file));
base::FilePath file_name0 = base::FilePath::FromUTF8Unsafe("0.txt");
ASSERT_TRUE(LocateAndOpenEntry(&reader, file_name0));
EXPECT_TRUE(reader.ExtractCurrentEntryToString(0, &contents));
EXPECT_EQ("", contents);
EXPECT_TRUE(reader.ExtractCurrentEntryToString(1, &contents));
EXPECT_EQ("", contents);
base::FilePath file_name1 = base::FilePath::FromUTF8Unsafe("1.txt");
ASSERT_TRUE(LocateAndOpenEntry(&reader, file_name1));
EXPECT_TRUE(reader.ExtractCurrentEntryToString(0, &contents));
EXPECT_EQ("", contents);
EXPECT_TRUE(reader.ExtractCurrentEntryToString(1, &contents));
EXPECT_EQ("0", contents);
EXPECT_TRUE(reader.ExtractCurrentEntryToString(2, &contents));
EXPECT_EQ("0", contents);
base::FilePath file_name4 = base::FilePath::FromUTF8Unsafe("4.txt");
ASSERT_TRUE(LocateAndOpenEntry(&reader, file_name4));
EXPECT_TRUE(reader.ExtractCurrentEntryToString(0, &contents));
EXPECT_EQ("", contents);
EXPECT_FALSE(reader.ExtractCurrentEntryToString(2, &contents));
EXPECT_EQ("01", contents);
EXPECT_TRUE(reader.ExtractCurrentEntryToString(4, &contents));
EXPECT_EQ("0123", contents);
// Checks that entire file is extracted and function returns true when
// |max_read_bytes| is larger than file size.
EXPECT_TRUE(reader.ExtractCurrentEntryToString(5, &contents));
EXPECT_EQ("0123", contents);
reader.Close();
}
TEST_F(ZipReaderTest, ExtractPosixPermissions) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("test_posix_permissions.zip")));
for (auto entry : {"0.txt", "1.txt", "2.txt", "3.txt"}) {
ASSERT_TRUE(LocateAndOpenEntry(&reader, base::FilePath::FromASCII(entry)));
FilePathWriterDelegate delegate(temp_dir.GetPath().AppendASCII(entry));
ASSERT_TRUE(reader.ExtractCurrentEntry(&delegate));
}
reader.Close();
#if defined(OS_POSIX)
// This assumes a umask of at least 0400.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_dir.GetPath().AppendASCII("0.txt"), &mode));
EXPECT_EQ(mode & 0700, 0700);
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_dir.GetPath().AppendASCII("1.txt"), &mode));
EXPECT_EQ(mode & 0700, 0600);
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_dir.GetPath().AppendASCII("2.txt"), &mode));
EXPECT_EQ(mode & 0700, 0700);
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_dir.GetPath().AppendASCII("3.txt"), &mode));
EXPECT_EQ(mode & 0700, 0600);
#endif
}
// This test exposes http://crbug.com/430959, at least on OS X
TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) {
for (int i = 0; i < 100000; ++i) {
FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
ZipReader reader;
ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
}
}
// Test that when WriterDelegate::PrepareMock returns false, no other methods on
// the delegate are called and the extraction fails.
TEST_F(ZipReaderTest, ExtractCurrentEntryPrepareFailure) {
testing::StrictMock<MockWriterDelegate> mock_writer;
EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(false));
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer));
}
// Test that when WriterDelegate::WriteBytes returns false, only the OnError
// method on the delegate is called and the extraction fails.
TEST_F(ZipReaderTest, ExtractCurrentEntryWriteBytesFailure) {
testing::StrictMock<MockWriterDelegate> mock_writer;
EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(true));
EXPECT_CALL(mock_writer, WriteBytes(_, _)).WillOnce(Return(false));
EXPECT_CALL(mock_writer, OnError());
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer));
}
// Test that extraction succeeds when the writer delegate reports all is well.
TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) {
testing::StrictMock<MockWriterDelegate> mock_writer;
EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(true));
EXPECT_CALL(mock_writer, WriteBytes(_, _)).WillRepeatedly(Return(true));
EXPECT_CALL(mock_writer, SetPosixFilePermissions(_));
EXPECT_CALL(mock_writer, SetTimeModified(_));
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
ASSERT_TRUE(reader.ExtractCurrentEntry(&mock_writer));
}
TEST_F(ZipReaderTest, WrongCrc) {
ZipReader reader;
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Wrong CRC.zip")));
const ZipReader::Entry* const entry =
LocateAndOpenEntry(&reader, base::FilePath::FromASCII("Corrupted.txt"));
ASSERT_TRUE(entry);
std::string contents = "dummy";
EXPECT_FALSE(reader.ExtractCurrentEntryToString(&contents));
EXPECT_EQ("This file has been changed after its CRC was computed.\n",
contents);
contents = "dummy";
EXPECT_FALSE(
reader.ExtractCurrentEntryToString(entry->original_size + 1, &contents));
EXPECT_EQ("This file has been changed after its CRC was computed.\n",
contents);
contents = "dummy";
EXPECT_FALSE(
reader.ExtractCurrentEntryToString(entry->original_size, &contents));
EXPECT_EQ("This file has been changed after its CRC was computed.\n",
contents);
contents = "dummy";
EXPECT_FALSE(
reader.ExtractCurrentEntryToString(entry->original_size - 1, &contents));
EXPECT_EQ("This file has been changed after its CRC was computed.", contents);
}
class FileWriterDelegateTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_));
file_.Initialize(temp_file_path_,
(base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
base::File::FLAG_WRITE | base::File::FLAG_WIN_TEMPORARY |
base::File::FLAG_DELETE_ON_CLOSE));
ASSERT_TRUE(file_.IsValid());
}
base::FilePath temp_file_path_;
base::File file_;
};
TEST_F(FileWriterDelegateTest, WriteToEnd) {
const std::string payload = "This is the actualy payload data.\n";
{
FileWriterDelegate writer(&file_);
EXPECT_EQ(0, writer.file_length());
ASSERT_TRUE(writer.PrepareOutput());
ASSERT_TRUE(writer.WriteBytes(payload.data(), payload.size()));
EXPECT_EQ(payload.size(), writer.file_length());
}
EXPECT_EQ(payload.size(), file_.GetLength());
}
TEST_F(FileWriterDelegateTest, EmptyOnError) {
const std::string payload = "This is the actualy payload data.\n";
{
FileWriterDelegate writer(&file_);
EXPECT_EQ(0, writer.file_length());
ASSERT_TRUE(writer.PrepareOutput());
ASSERT_TRUE(writer.WriteBytes(payload.data(), payload.size()));
EXPECT_EQ(payload.size(), writer.file_length());
EXPECT_EQ(payload.size(), file_.GetLength());
writer.OnError();
EXPECT_EQ(0, writer.file_length());
}
EXPECT_EQ(0, file_.GetLength());
}
} // namespace zip

1433
deps/zlib/google/zip_unittest.cc vendored Normal file

File diff suppressed because it is too large Load Diff

309
deps/zlib/google/zip_writer.cc vendored Normal file
View File

@ -0,0 +1,309 @@
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip_writer.h"
#include <algorithm>
#include <tuple>
#include "base/files/file.h"
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "third_party/zlib/google/redact.h"
#include "third_party/zlib/google/zip_internal.h"
namespace zip {
namespace internal {
bool ZipWriter::ShouldContinue() {
if (!progress_callback_)
return true;
const base::TimeTicks now = base::TimeTicks::Now();
if (next_progress_report_time_ > now)
return true;
next_progress_report_time_ = now + progress_period_;
if (progress_callback_.Run(progress_))
return true;
LOG(ERROR) << "Cancelling ZIP creation";
return false;
}
bool ZipWriter::AddFileContent(const base::FilePath& path, base::File file) {
char buf[zip::internal::kZipBufSize];
while (ShouldContinue()) {
const int num_bytes =
file.ReadAtCurrentPos(buf, zip::internal::kZipBufSize);
if (num_bytes < 0) {
PLOG(ERROR) << "Cannot read file " << Redact(path);
return false;
}
if (num_bytes == 0)
return true;
if (zipWriteInFileInZip(zip_file_, buf, num_bytes) != ZIP_OK) {
PLOG(ERROR) << "Cannot write data from file " << Redact(path)
<< " to ZIP";
return false;
}
progress_.bytes += num_bytes;
}
return false;
}
bool ZipWriter::OpenNewFileEntry(const base::FilePath& path,
bool is_directory,
base::Time last_modified) {
std::string str_path = path.AsUTF8Unsafe();
#if defined(OS_WIN)
base::ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
#endif
Compression compression = kDeflated;
if (is_directory) {
str_path += "/";
} else {
compression = GetCompressionMethod(path);
}
return zip::internal::ZipOpenNewFileInZip(zip_file_, str_path, last_modified,
compression);
}
bool ZipWriter::CloseNewFileEntry() {
return zipCloseFileInZip(zip_file_) == ZIP_OK;
}
bool ZipWriter::AddFileEntry(const base::FilePath& path, base::File file) {
base::File::Info info;
if (!file.GetInfo(&info))
return false;
if (!OpenNewFileEntry(path, /*is_directory=*/false, info.last_modified))
return false;
if (!AddFileContent(path, std::move(file)))
return false;
progress_.files++;
return CloseNewFileEntry();
}
bool ZipWriter::AddDirectoryEntry(const base::FilePath& path) {
FileAccessor::Info info;
if (!file_accessor_->GetInfo(path, &info) || !info.is_directory) {
LOG(ERROR) << "Not a directory: " << Redact(path);
progress_.errors++;
return continue_on_error_;
}
if (!OpenNewFileEntry(path, /*is_directory=*/true, info.last_modified))
return false;
if (!CloseNewFileEntry())
return false;
progress_.directories++;
if (!ShouldContinue())
return false;
if (!recursive_)
return true;
return AddDirectoryContents(path);
}
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// static
std::unique_ptr<ZipWriter> ZipWriter::CreateWithFd(
int zip_file_fd,
FileAccessor* file_accessor) {
DCHECK(zip_file_fd != base::kInvalidPlatformFile);
zipFile zip_file =
internal::OpenFdForZipping(zip_file_fd, APPEND_STATUS_CREATE);
if (!zip_file) {
DLOG(ERROR) << "Cannot create ZIP file for FD " << zip_file_fd;
return nullptr;
}
return std::unique_ptr<ZipWriter>(new ZipWriter(zip_file, file_accessor));
}
#endif
// static
std::unique_ptr<ZipWriter> ZipWriter::Create(
const base::FilePath& zip_file_path,
FileAccessor* file_accessor) {
DCHECK(!zip_file_path.empty());
zipFile zip_file = internal::OpenForZipping(zip_file_path.AsUTF8Unsafe(),
APPEND_STATUS_CREATE);
if (!zip_file) {
PLOG(ERROR) << "Cannot create ZIP file " << Redact(zip_file_path);
return nullptr;
}
return std::unique_ptr<ZipWriter>(new ZipWriter(zip_file, file_accessor));
}
ZipWriter::ZipWriter(zipFile zip_file, FileAccessor* file_accessor)
: zip_file_(zip_file), file_accessor_(file_accessor) {}
ZipWriter::~ZipWriter() {
if (zip_file_)
zipClose(zip_file_, nullptr);
}
bool ZipWriter::Close() {
const bool success = zipClose(zip_file_, nullptr) == ZIP_OK;
zip_file_ = nullptr;
// Call the progress callback one last time with the final progress status.
if (progress_callback_ && !progress_callback_.Run(progress_)) {
LOG(ERROR) << "Cancelling ZIP creation at the end";
return false;
}
return success;
}
bool ZipWriter::AddMixedEntries(Paths paths) {
// Pointers to directory paths in |paths|.
std::vector<const base::FilePath*> directories;
// Declared outside loop to reuse internal buffer.
std::vector<base::File> files;
// First pass. We don't know which paths are files and which ones are
// directories, and we want to avoid making a call to file_accessor_ for each
// path. Try to open all of the paths as files. We'll get invalid file
// descriptors for directories, and we'll process these directories in the
// second pass.
while (!paths.empty()) {
// Work with chunks of 50 paths at most.
const size_t n = std::min<size_t>(paths.size(), 50);
Paths relative_paths;
std::tie(relative_paths, paths) = paths.split_at(n);
files.clear();
if (!file_accessor_->Open(relative_paths, &files) || files.size() != n)
return false;
for (size_t i = 0; i < n; i++) {
const base::FilePath& relative_path = relative_paths[i];
DCHECK(!relative_path.empty());
base::File& file = files[i];
if (file.IsValid()) {
// It's a file.
if (!AddFileEntry(relative_path, std::move(file)))
return false;
} else {
// It's probably a directory. Remember its path for the second pass.
directories.push_back(&relative_path);
}
}
}
// Second pass for directories discovered during the first pass.
for (const base::FilePath* const path : directories) {
DCHECK(path);
if (!AddDirectoryEntry(*path))
return false;
}
return true;
}
bool ZipWriter::AddFileEntries(Paths paths) {
// Declared outside loop to reuse internal buffer.
std::vector<base::File> files;
while (!paths.empty()) {
// Work with chunks of 50 paths at most.
const size_t n = std::min<size_t>(paths.size(), 50);
Paths relative_paths;
std::tie(relative_paths, paths) = paths.split_at(n);
DCHECK_EQ(relative_paths.size(), n);
files.clear();
if (!file_accessor_->Open(relative_paths, &files) || files.size() != n)
return false;
for (size_t i = 0; i < n; i++) {
const base::FilePath& relative_path = relative_paths[i];
DCHECK(!relative_path.empty());
base::File& file = files[i];
if (!file.IsValid()) {
LOG(ERROR) << "Cannot open " << Redact(relative_path);
progress_.errors++;
if (continue_on_error_)
continue;
return false;
}
if (!AddFileEntry(relative_path, std::move(file)))
return false;
}
}
return true;
}
bool ZipWriter::AddDirectoryEntries(Paths paths) {
for (const base::FilePath& path : paths) {
if (!AddDirectoryEntry(path))
return false;
}
return true;
}
bool ZipWriter::AddDirectoryContents(const base::FilePath& path) {
std::vector<base::FilePath> files, subdirs;
if (!file_accessor_->List(path, &files, &subdirs)) {
progress_.errors++;
return continue_on_error_;
}
Filter(&files);
Filter(&subdirs);
if (!AddFileEntries(files))
return false;
return AddDirectoryEntries(subdirs);
}
void ZipWriter::Filter(std::vector<base::FilePath>* const paths) {
DCHECK(paths);
if (!filter_callback_)
return;
const auto end = std::remove_if(paths->begin(), paths->end(),
[this](const base::FilePath& path) {
return !filter_callback_.Run(path);
});
paths->erase(end, paths->end());
}
} // namespace internal
} // namespace zip

154
deps/zlib/google/zip_writer.h vendored Normal file
View File

@ -0,0 +1,154 @@
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_WRITER_H_
#define THIRD_PARTY_ZLIB_GOOGLE_ZIP_WRITER_H_
#include <memory>
#include <vector>
#include "base/files/file_path.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "third_party/zlib/google/zip.h"
#if defined(USE_SYSTEM_MINIZIP)
#include <minizip/unzip.h>
#include <minizip/zip.h>
#else
#include "third_party/zlib/contrib/minizip/unzip.h"
#include "third_party/zlib/contrib/minizip/zip.h"
#endif
namespace zip {
namespace internal {
// A class used to write entries to a ZIP file and buffering the reading of
// files to limit the number of calls to the FileAccessor. This is for
// performance reasons as these calls may be expensive when IPC based).
// This class is so far internal and only used by zip.cc, but could be made
// public if needed.
//
// All methods returning a bool return true on success and false on error.
class ZipWriter {
public:
// Creates a writer that will write a ZIP file to |zip_file_fd| or |zip_file|
// and which entries are relative to |file_accessor|'s source directory.
// All file reads are performed using |file_accessor|.
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
static std::unique_ptr<ZipWriter> CreateWithFd(int zip_file_fd,
FileAccessor* file_accessor);
#endif
static std::unique_ptr<ZipWriter> Create(const base::FilePath& zip_file,
FileAccessor* file_accessor);
ZipWriter(const ZipWriter&) = delete;
ZipWriter& operator=(const ZipWriter&) = delete;
~ZipWriter();
// Sets the optional progress callback. The callback is called once for each
// time |period|. The final callback is always called when the ZIP operation
// completes.
void SetProgressCallback(ProgressCallback callback, base::TimeDelta period) {
progress_callback_ = std::move(callback);
progress_period_ = std::move(period);
}
// Should ignore missing files and directories?
void ContinueOnError(bool continue_on_error) {
continue_on_error_ = continue_on_error;
}
// Sets the recursive flag, indicating whether the contents of subdirectories
// should be included.
void SetRecursive(bool b) { recursive_ = b; }
// Sets the filter callback.
void SetFilterCallback(FilterCallback callback) {
filter_callback_ = std::move(callback);
}
// Adds the contents of a directory. If the recursive flag is set, the
// contents of subdirectories are also added.
bool AddDirectoryContents(const base::FilePath& path);
// Adds the entries at |paths| to the ZIP file. These can be a mixed bag of
// files and directories. If the recursive flag is set, the contents of
// subdirectories is also added.
bool AddMixedEntries(Paths paths);
// Closes the ZIP file.
bool Close();
private:
// Takes ownership of |zip_file|.
ZipWriter(zipFile zip_file, FileAccessor* file_accessor);
// Regularly called during processing to check whether zipping should continue
// or should be cancelled.
bool ShouldContinue();
// Adds file content to currently open file entry.
bool AddFileContent(const base::FilePath& path, base::File file);
// Adds a file entry (including file contents).
bool AddFileEntry(const base::FilePath& path, base::File file);
// Adds file entries. All the paths should be existing files.
bool AddFileEntries(Paths paths);
// Adds a directory entry. If the recursive flag is set, the contents of this
// directory are also added.
bool AddDirectoryEntry(const base::FilePath& path);
// Adds directory entries. All the paths should be existing directories. If
// the recursive flag is set, the contents of these directories are also
// added.
bool AddDirectoryEntries(Paths paths);
// Opens a file or directory entry.
bool OpenNewFileEntry(const base::FilePath& path,
bool is_directory,
base::Time last_modified);
// Closes the currently open entry.
bool CloseNewFileEntry();
// Filters entries.
void Filter(std::vector<base::FilePath>* paths);
// The actual zip file.
zipFile zip_file_;
// Abstraction over file access methods used to read files.
FileAccessor* const file_accessor_;
// Progress stats.
Progress progress_;
// Optional progress callback.
ProgressCallback progress_callback_;
// Optional progress reporting period.
base::TimeDelta progress_period_;
// Next time to report progress.
base::TimeTicks next_progress_report_time_ = base::TimeTicks::Now();
// Filter used to exclude files from the ZIP file.
FilterCallback filter_callback_;
// Should recursively add directories?
bool recursive_ = false;
// Should ignore missing files and directories?
bool continue_on_error_ = false;
};
} // namespace internal
} // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_WRITER_H_