From c0a30fca366995e2d5ec177bf864ff156dbd9b71 Mon Sep 17 00:00:00 2001 From: Fabio Carneiro Date: Fri, 1 Apr 2022 19:48:58 +0200 Subject: [PATCH] deps: update chromium zlib 1.2.12 --- deps/zlib/BUILD.gn | 257 +++- deps/zlib/OWNERS | 6 +- deps/zlib/README.chromium | 4 +- deps/zlib/adler32.c | 12 +- deps/zlib/adler32_simd.c | 4 - deps/zlib/arm_features.c | 90 -- deps/zlib/chromeconf.h | 4 + deps/zlib/contrib/bench/zlib_bench.cc | 83 +- deps/zlib/contrib/minizip/iowin32.c | 8 +- deps/zlib/contrib/minizip/miniunz.c | 13 +- deps/zlib/contrib/minizip/minizip.c | 7 +- deps/zlib/contrib/minizip/minizip.md | 9 + deps/zlib/contrib/optimizations/chunkcopy.h | 34 +- .../contrib/optimizations/inffast_chunk.c | 30 +- .../contrib/optimizations/insert_string.h | 69 +- deps/zlib/contrib/tests/DEPS | 4 + deps/zlib/contrib/tests/OWNERS | 1 + deps/zlib/contrib/tests/fuzzers/BUILD.gn | 38 +- deps/zlib/contrib/tests/fuzzers/OWNERS | 1 - .../contrib/tests/fuzzers/deflate_fuzzer.cc | 75 +- .../tests/fuzzers/streaming_inflate_fuzzer.cc | 74 + deps/zlib/contrib/tests/infcover.cc | 686 +++++++++ deps/zlib/contrib/tests/infcover.h | 11 + deps/zlib/contrib/tests/run_all_unittests.cc | 14 + deps/zlib/contrib/tests/utils_unittest.cc | 1013 +++++++++++++ deps/zlib/cpu_features.c | 167 +++ deps/zlib/{arm_features.h => cpu_features.h} | 9 +- deps/zlib/crc32.c | 17 +- deps/zlib/crc32_simd.c | 4 - deps/zlib/crc_folding.c | 18 +- deps/zlib/deflate.c | 87 +- deps/zlib/fill_window_sse.c | 8 +- deps/zlib/google/BUILD.gn | 8 +- deps/zlib/google/DEPS | 1 + deps/zlib/google/OWNERS | 3 + deps/zlib/google/compression_utils.cc | 39 +- deps/zlib/google/compression_utils.h | 28 +- .../zlib/google/compression_utils_portable.cc | 4 +- deps/zlib/google/compression_utils_portable.h | 6 +- .../zlib/google/compression_utils_unittest.cc | 25 +- deps/zlib/google/redact.h | 31 + .../test/data/Different Encryptions.zip | Bin 0 -> 1083 bytes .../test/data/Empty Dir Same Name As File.zip | Bin 0 -> 218 bytes deps/zlib/google/test/data/Mixed Paths.zip | Bin 0 -> 12814 bytes .../data/Parent Dir Same Name As File.zip | Bin 0 -> 241 bytes deps/zlib/google/test/data/README.md | 15 + .../google/test/data/Repeated Dir Name.zip | Bin 0 -> 210 bytes ...epeated File Name With Different Cases.zip | Bin 0 -> 313 bytes .../google/test/data/Repeated File Name.zip | Bin 0 -> 227 bytes .../zlib/google/test/data/SJIS Bug 846195.zip | Bin 0 -> 479 bytes .../test/data/Windows Special Names.zip | Bin 0 -> 3985 bytes deps/zlib/google/test/data/Wrong CRC.zip | Bin 0 -> 231 bytes deps/zlib/google/test/data/empty.zip | Bin 0 -> 22 bytes .../test/data/test_posix_permissions.zip | Bin 0 -> 574 bytes deps/zlib/google/zip.cc | 318 ++--- deps/zlib/google/zip.h | 236 +-- deps/zlib/google/zip_internal.cc | 242 +++- deps/zlib/google/zip_internal.h | 22 +- deps/zlib/google/zip_reader.cc | 704 +++++---- deps/zlib/google/zip_reader.h | 345 +++-- deps/zlib/google/zip_reader_unittest.cc | 721 +++++++--- deps/zlib/google/zip_unittest.cc | 1272 ++++++++++++++--- deps/zlib/google/zip_writer.cc | 335 +++-- deps/zlib/google/zip_writer.h | 131 +- deps/zlib/gzguts.h | 2 +- deps/zlib/gzlib.c | 4 +- deps/zlib/gzread.c | 10 +- deps/zlib/gzwrite.c | 15 +- deps/zlib/inffast.c | 28 +- .../zlib/patches/0003-uninitializedjump.patch | 15 + deps/zlib/patches/0004-fix-uwp.patch | 22 + deps/zlib/patches/0005-infcover-gtest.patch | 405 ++++++ deps/zlib/patches/0006-fix-check_match.patch | 42 + .../0007-zero-init-deflate-window.patch | 40 + .../0008-minizip-zip-unzip-tools.patch | 98 ++ deps/zlib/patches/0009-infcover-oob.patch | 24 + deps/zlib/simd_stub.c | 35 - deps/zlib/win32/zlib.def | 94 -- deps/zlib/x86.c | 101 -- deps/zlib/x86.h | 16 - deps/zlib/zconf.h | 2 +- deps/zlib/zlib.gyp | 6 +- deps/zlib/zlib.h | 13 +- deps/zlib/zutil.c | 4 +- 84 files changed, 6308 insertions(+), 2011 deletions(-) delete mode 100644 deps/zlib/arm_features.c create mode 100644 deps/zlib/contrib/minizip/minizip.md create mode 100644 deps/zlib/contrib/tests/DEPS create mode 100644 deps/zlib/contrib/tests/fuzzers/streaming_inflate_fuzzer.cc create mode 100644 deps/zlib/contrib/tests/infcover.cc create mode 100644 deps/zlib/contrib/tests/infcover.h create mode 100644 deps/zlib/contrib/tests/run_all_unittests.cc create mode 100644 deps/zlib/contrib/tests/utils_unittest.cc create mode 100644 deps/zlib/cpu_features.c rename deps/zlib/{arm_features.h => cpu_features.h} (53%) create mode 100644 deps/zlib/google/redact.h create mode 100644 deps/zlib/google/test/data/Different Encryptions.zip create mode 100644 deps/zlib/google/test/data/Empty Dir Same Name As File.zip create mode 100644 deps/zlib/google/test/data/Mixed Paths.zip create mode 100644 deps/zlib/google/test/data/Parent Dir Same Name As File.zip create mode 100644 deps/zlib/google/test/data/README.md create mode 100644 deps/zlib/google/test/data/Repeated Dir Name.zip create mode 100644 deps/zlib/google/test/data/Repeated File Name With Different Cases.zip create mode 100644 deps/zlib/google/test/data/Repeated File Name.zip create mode 100644 deps/zlib/google/test/data/SJIS Bug 846195.zip create mode 100644 deps/zlib/google/test/data/Windows Special Names.zip create mode 100644 deps/zlib/google/test/data/Wrong CRC.zip create mode 100644 deps/zlib/google/test/data/empty.zip create mode 100644 deps/zlib/google/test/data/test_posix_permissions.zip create mode 100644 deps/zlib/patches/0003-uninitializedjump.patch create mode 100644 deps/zlib/patches/0004-fix-uwp.patch create mode 100644 deps/zlib/patches/0005-infcover-gtest.patch create mode 100644 deps/zlib/patches/0006-fix-check_match.patch create mode 100644 deps/zlib/patches/0007-zero-init-deflate-window.patch create mode 100644 deps/zlib/patches/0008-minizip-zip-unzip-tools.patch create mode 100644 deps/zlib/patches/0009-infcover-oob.patch delete mode 100644 deps/zlib/simd_stub.c delete mode 100644 deps/zlib/win32/zlib.def delete mode 100644 deps/zlib/x86.c delete mode 100644 deps/zlib/x86.h diff --git a/deps/zlib/BUILD.gn b/deps/zlib/BUILD.gn index 5f8873357d35d1..49f52e1f8b1e50 100644 --- a/deps/zlib/BUILD.gn +++ b/deps/zlib/BUILD.gn @@ -4,6 +4,10 @@ import("//build/config/compiler/compiler.gni") +if (build_with_chromium) { + import("//testing/test.gni") +} + if (current_cpu == "arm" || current_cpu == "arm64") { import("//build/config/arm.gni") } @@ -14,10 +18,47 @@ config("zlib_config") { config("zlib_internal_config") { defines = [ "ZLIB_IMPLEMENTATION" ] + + if (!is_debug) { + # Build code using -O3, see: crbug.com/1084371. + configs = [ "//build/config/compiler:optimize_speed" ] + } + if (is_debug || use_libfuzzer) { + # Enable zlib's asserts in debug and fuzzer builds. + defines += [ "ZLIB_DEBUG" ] + } + + if (is_win && !is_clang) { + # V8 supports building with msvc, these silence some warnings that + # causes compilation to fail (https://crbug.com/1255096). + cflags = [ + "/wd4244", + "/wd4100", + "/wd4702", + "/wd4127", + ] + } +} + +source_set("zlib_common_headers") { + sources = [ + "chromeconf.h", + "deflate.h", + "inffast.h", + "inffixed.h", + "inflate.h", + "inftrees.h", + "zconf.h", + "zlib.h", + "zutil.h", + ] } use_arm_neon_optimizations = false -if (current_cpu == "arm" || current_cpu == "arm64") { +if ((current_cpu == "arm" || current_cpu == "arm64") && + !(is_win && !is_clang)) { + # TODO(richard.townsend@arm.com): Optimizations temporarily disabled for + # Windows on Arm MSVC builds, see http://crbug.com/v8/10012. if (arm_use_neon) { use_arm_neon_optimizations = true } @@ -29,6 +70,11 @@ use_x86_x64_optimizations = config("zlib_adler32_simd_config") { if (use_x86_x64_optimizations) { defines = [ "ADLER32_SIMD_SSSE3" ] + if (is_win) { + defines += [ "X86_WINDOWS" ] + } else { + defines += [ "X86_NOT_WINDOWS" ] + } } if (use_arm_neon_optimizations) { @@ -55,16 +101,13 @@ source_set("zlib_adler32_simd") { "adler32_simd.c", "adler32_simd.h", ] - if (!is_debug) { - # Use optimize_speed (-O3) to output the _smallest_ code. - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } } configs += [ ":zlib_internal_config" ] public_configs = [ ":zlib_adler32_simd_config" ] + + public_deps = [ ":zlib_common_headers" ] } if (use_arm_neon_optimizations) { @@ -78,6 +121,8 @@ if (use_arm_neon_optimizations) { defines += [ "ARMV8_OS_ANDROID" ] } else if (is_linux || is_chromeos) { defines += [ "ARMV8_OS_LINUX" ] + } else if (is_mac) { + defines += [ "ARMV8_OS_MACOS" ] } else if (is_fuchsia) { defines += [ "ARMV8_OS_FUCHSIA" ] } else if (is_win) { @@ -94,37 +139,23 @@ if (use_arm_neon_optimizations) { if (!is_ios) { include_dirs = [ "." ] - if (is_android) { - import("//build/config/android/config.gni") - if (defined(android_ndk_root) && android_ndk_root != "") { - deps = [ - "//third_party/android_ndk:cpu_features", - ] - } else { - assert(false, "CPU detection requires the Android NDK") - } - } else if (!is_win && !is_clang) { + if (!is_win && !is_clang) { assert(!use_thin_lto, "ThinLTO fails mixing different module-level targets") cflags_c = [ "-march=armv8-a+crc" ] } sources = [ - "arm_features.c", - "arm_features.h", "crc32_simd.c", "crc32_simd.h", ] - - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } } configs += [ ":zlib_internal_config" ] public_configs = [ ":zlib_arm_crc32_config" ] + + public_deps = [ ":zlib_common_headers" ] } } @@ -139,6 +170,7 @@ config("zlib_inflate_chunk_simd_config") { if (use_arm_neon_optimizations) { defines = [ "INFLATE_CHUNK_SIMD_NEON" ] + if (current_cpu == "arm64") { defines += [ "INFLATE_CHUNK_READ_64LE" ] } @@ -157,22 +189,18 @@ source_set("zlib_inflate_chunk_simd") { "contrib/optimizations/inffast_chunk.h", "contrib/optimizations/inflate.c", ] - - if (use_arm_neon_optimizations && !is_debug) { - # Here we trade better performance on newer/bigger ARMv8 cores - # for less perf on ARMv7, per crbug.com/772870#c40 - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } } + configs += [ ":zlib_internal_config" ] + + # Needed for MSVC, which is still supported by V8 and PDFium. zlib uses K&R C + # style function declarations, which triggers warning C4131. configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ - ":zlib_internal_config", - "//build/config/compiler:no_chromium_code", - ] + configs += [ "//build/config/compiler:no_chromium_code" ] public_configs = [ ":zlib_inflate_chunk_simd_config" ] + + public_deps = [ ":zlib_common_headers" ] } config("zlib_crc32_simd_config") { @@ -201,6 +229,16 @@ source_set("zlib_crc32_simd") { configs += [ ":zlib_internal_config" ] public_configs = [ ":zlib_crc32_simd_config" ] + public_deps = [ ":zlib_common_headers" ] +} + +config("zlib_x86_simd_config") { + if (use_x86_x64_optimizations) { + defines = [ + "CRC32_SIMD_SSE42_PCLMUL", + "DEFLATE_FILL_WINDOW_SSE2", + ] + } } source_set("zlib_x86_simd") { @@ -218,17 +256,13 @@ source_set("zlib_x86_simd") { "-mpclmul", ] } - } else { - sources = [ - "simd_stub.c", - ] } - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ - ":zlib_internal_config", - "//build/config/compiler:no_chromium_code", - ] + configs += [ ":zlib_internal_config" ] + + public_configs = [ ":zlib_x86_simd_config" ] + + public_deps = [ ":zlib_common_headers" ] } config("zlib_warnings") { @@ -248,6 +282,8 @@ component("zlib") { "chromeconf.h", "compress.c", "contrib/optimizations/insert_string.h", + "cpu_features.c", + "cpu_features.h", "crc32.c", "crc32.h", "deflate.c", @@ -267,7 +303,6 @@ component("zlib") { "trees.c", "trees.h", "uncompr.c", - "x86.h", "zconf.h", "zlib.h", "zutil.c", @@ -277,6 +312,20 @@ component("zlib") { defines = [] deps = [] + if (!use_x86_x64_optimizations && !use_arm_neon_optimizations) { + # Apparently android_cronet bot builds with NEON disabled and + # we also should disable optimizations for iOS@x86 (a.k.a. simulator). + defines += [ "CPU_NO_SIMD" ] + } + + if (is_ios) { + # iOS@ARM is a special case where we always have NEON but don't check + # for crypto extensions. + # TODO(cavalcantii): verify what is the current state of CPU features + # shipped on latest iOS devices. + defines += [ "ARM_OS_IOS" ] + } + if (use_x86_x64_optimizations || use_arm_neon_optimizations) { deps += [ ":zlib_adler32_simd", @@ -284,7 +333,6 @@ component("zlib") { ] if (use_x86_x64_optimizations) { - sources += [ "x86.c" ] deps += [ ":zlib_crc32_simd" ] } else if (use_arm_neon_optimizations) { sources += [ "contrib/optimizations/slide_hash_neon.h" ] @@ -294,18 +342,29 @@ component("zlib") { sources += [ "inflate.c" ] } + deps += [ ":zlib_x86_simd" ] + + if (is_android) { + import("//build/config/android/config.gni") + if (defined(android_ndk_root) && android_ndk_root != "") { + deps += [ "//third_party/android_ndk:cpu_features" ] + } else { + assert(false, "CPU detection requires the Android NDK") + } + } + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + public_configs = [ ":zlib_config" ] + configs += [ ":zlib_internal_config", - "//build/config/compiler:no_chromium_code", # Must be after no_chromium_code for warning flags to be ordered correctly. ":zlib_warnings", ] - public_configs = [ ":zlib_config" ] - - deps += [ ":zlib_x86_simd" ] allow_circular_includes_from = deps } @@ -337,43 +396,113 @@ static_library("minizip") { ] } - if (is_mac || is_ios || is_android || is_nacl) { + if (is_apple || is_android || is_nacl) { # Mac, Android and the BSDs don't have fopen64, ftello64, or fseeko64. We # use fopen, ftell, and fseek instead on these systems. defines = [ "USE_FILE32API" ] } - deps = [ - ":zlib", - ] + deps = [ ":zlib" ] configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ - "//build/config/compiler:no_chromium_code", + configs += [ "//build/config/compiler:no_chromium_code" ] + + public_configs = [ ":zlib_config" ] + configs += [ # Must be after no_chromium_code for warning flags to be ordered correctly. ":minizip_warnings", ] - - public_configs = [ ":zlib_config" ] } executable("zlib_bench") { include_dirs = [ "." ] - sources = [ - "contrib/bench/zlib_bench.cc", - ] - + sources = [ "contrib/bench/zlib_bench.cc" ] if (!is_debug) { configs -= [ "//build/config/compiler:default_optimization" ] configs += [ "//build/config/compiler:optimize_speed" ] } + deps = [ ":zlib" ] + configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] +} - deps = [ - ":zlib", - ] +if (!is_win || target_os != "winuwp") { + executable("minizip_bin") { + include_dirs = [ "." ] + + sources = [ "contrib/minizip/minizip.c" ] + + if (is_clang) { + cflags = [ "-Wno-incompatible-pointer-types-discards-qualifiers" ] + } + + if (!is_debug) { + configs -= [ "//build/config/compiler:default_optimization" ] + configs += [ "//build/config/compiler:optimize_speed" ] + } + + deps = [ ":minizip" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + + executable("miniunz_bin") { + include_dirs = [ "." ] + + sources = [ "contrib/minizip/miniunz.c" ] + + if (is_clang) { + cflags = [ "-Wno-incompatible-pointer-types-discards-qualifiers" ] + } + + if (!is_debug) { + configs -= [ "//build/config/compiler:default_optimization" ] + configs += [ "//build/config/compiler:optimize_speed" ] + } + + deps = [ ":minizip" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } +} + +if (build_with_chromium) { + test("zlib_unittests") { + testonly = true + + sources = [ + "contrib/tests/infcover.cc", + "contrib/tests/infcover.h", + "contrib/tests/run_all_unittests.cc", + "contrib/tests/utils_unittest.cc", + "google/compression_utils_unittest.cc", + "google/zip_reader_unittest.cc", + "google/zip_unittest.cc", + ] + + data = [ "google/test/data/" ] + + deps = [ + ":zlib", + "google:compression_utils", + "google:zip", + "//base/test:test_support", + "//testing/gtest", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + include_dirs = [ + "//third_party/googletest/src/googletest/include/gtest", + ".", + "google", + ] + } } diff --git a/deps/zlib/OWNERS b/deps/zlib/OWNERS index 069bcd8c2a1445..ecffb59f051501 100644 --- a/deps/zlib/OWNERS +++ b/deps/zlib/OWNERS @@ -1,7 +1,5 @@ agl@chromium.org cavalcantii@chromium.org cblume@chromium.org -mtklein@chromium.org -scroggo@chromium.org - -# COMPONENT: Internals +noel@chromium.org +scroggo@google.com diff --git a/deps/zlib/README.chromium b/deps/zlib/README.chromium index 3d90f79be2ffc2..db159bef60a9d9 100644 --- a/deps/zlib/README.chromium +++ b/deps/zlib/README.chromium @@ -1,7 +1,8 @@ Name: zlib Short Name: zlib URL: http://zlib.net/ -Version: 1.2.11 +Version: 1.2.12 +CPEPrefix: cpe:/a:zlib:zlib:1.2.11 Security Critical: yes License: Custom license License File: LICENSE @@ -26,3 +27,4 @@ Local Modifications: - Plus the changes in 'patches' folder. - Code in contrib/ other than contrib/minizip was added to match zlib's contributor layout. + - Backported patches from 1.2.12 release (Work In Progress). diff --git a/deps/zlib/adler32.c b/deps/zlib/adler32.c index a42f35fce2a2e0..8f8fbb904891ae 100644 --- a/deps/zlib/adler32.c +++ b/deps/zlib/adler32.c @@ -59,10 +59,8 @@ local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); # define MOD63(a) a %= BASE #endif -#if defined(ADLER32_SIMD_SSSE3) -#include "adler32_simd.h" -#include "x86.h" -#elif defined(ADLER32_SIMD_NEON) +#include "cpu_features.h" +#if defined(ADLER32_SIMD_SSSE3) || defined(ADLER32_SIMD_NEON) #include "adler32_simd.h" #endif @@ -76,10 +74,10 @@ uLong ZEXPORT adler32_z(adler, buf, len) unsigned n; #if defined(ADLER32_SIMD_SSSE3) - if (x86_cpu_enable_ssse3 && buf && len >= 64) + if (buf != Z_NULL && len >= 64 && x86_cpu_enable_ssse3) return adler32_simd_(adler, buf, len); #elif defined(ADLER32_SIMD_NEON) - if (buf && len >= 64) + if (buf != Z_NULL && len >= 64) return adler32_simd_(adler, buf, len); #endif @@ -108,7 +106,7 @@ uLong ZEXPORT adler32_z(adler, buf, len) */ if (buf == Z_NULL) { if (!len) /* Assume user is calling adler32(0, NULL, 0); */ - x86_check_features(); + cpu_check_features(); return 1L; } #else diff --git a/deps/zlib/adler32_simd.c b/deps/zlib/adler32_simd.c index f8b07297b93840..1354915cc099ad 100644 --- a/deps/zlib/adler32_simd.c +++ b/deps/zlib/adler32_simd.c @@ -50,13 +50,9 @@ #define NMAX 5552 #if defined(ADLER32_SIMD_SSSE3) -#ifndef __GNUC__ -#define __attribute__() -#endif #include -__attribute__((target("ssse3"))) uint32_t ZLIB_INTERNAL adler32_simd_( /* SSSE3 */ uint32_t adler, const unsigned char *buf, diff --git a/deps/zlib/arm_features.c b/deps/zlib/arm_features.c deleted file mode 100644 index f5641c39bbce97..00000000000000 --- a/deps/zlib/arm_features.c +++ /dev/null @@ -1,90 +0,0 @@ -/* arm_features.c -- ARM processor features detection. - * - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the Chromium source repository LICENSE file. - */ - -#include "arm_features.h" -#include "zutil.h" -#include - -int ZLIB_INTERNAL arm_cpu_enable_crc32 = 0; -int ZLIB_INTERNAL arm_cpu_enable_pmull = 0; - -#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_FUCHSIA) -#include -#endif - -#if defined(ARMV8_OS_ANDROID) -#include -#elif defined(ARMV8_OS_LINUX) -#include -#include -#elif defined(ARMV8_OS_FUCHSIA) -#include -#include -#include -#elif defined(ARMV8_OS_WINDOWS) -#include -#else -#error arm_features.c ARM feature detection in not defined for your platform -#endif - -static void _arm_check_features(void); - -#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_FUCHSIA) -static pthread_once_t cpu_check_inited_once = PTHREAD_ONCE_INIT; -void ZLIB_INTERNAL arm_check_features(void) -{ - pthread_once(&cpu_check_inited_once, _arm_check_features); -} -#elif defined(ARMV8_OS_WINDOWS) -static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT; -static BOOL CALLBACK _arm_check_features_forwarder(PINIT_ONCE once, PVOID param, PVOID* context) -{ - _arm_check_features(); - return TRUE; -} -void ZLIB_INTERNAL arm_check_features(void) -{ - InitOnceExecuteOnce(&cpu_check_inited_once, _arm_check_features_forwarder, - NULL, NULL); -} -#endif - -/* - * See http://bit.ly/2CcoEsr for run-time detection of ARM features and also - * crbug.com/931275 for android_getCpuFeatures() use in the Android sandbox. - */ -static void _arm_check_features(void) -{ -#if defined(ARMV8_OS_ANDROID) && defined(__aarch64__) - uint64_t features = android_getCpuFeatures(); - arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM64_FEATURE_CRC32); - arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM64_FEATURE_PMULL); -#elif defined(ARMV8_OS_ANDROID) /* aarch32 */ - uint64_t features = android_getCpuFeatures(); - arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM_FEATURE_CRC32); - arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM_FEATURE_PMULL); -#elif defined(ARMV8_OS_LINUX) && defined(__aarch64__) - unsigned long features = getauxval(AT_HWCAP); - arm_cpu_enable_crc32 = !!(features & HWCAP_CRC32); - arm_cpu_enable_pmull = !!(features & HWCAP_PMULL); -#elif defined(ARMV8_OS_LINUX) && (defined(__ARM_NEON) || defined(__ARM_NEON__)) - /* Query HWCAP2 for ARMV8-A SoCs running in aarch32 mode */ - unsigned long features = getauxval(AT_HWCAP2); - arm_cpu_enable_crc32 = !!(features & HWCAP2_CRC32); - arm_cpu_enable_pmull = !!(features & HWCAP2_PMULL); -#elif defined(ARMV8_OS_FUCHSIA) - uint32_t features; - zx_status_t rc = zx_system_get_features(ZX_FEATURE_KIND_CPU, &features); - if (rc != ZX_OK || (features & ZX_ARM64_FEATURE_ISA_ASIMD) == 0) - return; /* Report nothing if ASIMD(NEON) is missing */ - arm_cpu_enable_crc32 = !!(features & ZX_ARM64_FEATURE_ISA_CRC32); - arm_cpu_enable_pmull = !!(features & ZX_ARM64_FEATURE_ISA_PMULL); -#elif defined(ARMV8_OS_WINDOWS) - arm_cpu_enable_crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE); - arm_cpu_enable_pmull = IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); -#endif -} diff --git a/deps/zlib/chromeconf.h b/deps/zlib/chromeconf.h index 666093d696a583..5ecf29edbff5ae 100644 --- a/deps/zlib/chromeconf.h +++ b/deps/zlib/chromeconf.h @@ -192,4 +192,8 @@ #define arm_check_features Cr_z_arm_check_features #define armv8_crc32_little Cr_z_armv8_crc32_little +/* Symbols added by cpu_features.c */ +#define cpu_check_features Cr_z_cpu_check_features +#define x86_cpu_enable_sse2 Cr_z_x86_cpu_enable_sse2 + #endif /* THIRD_PARTY_ZLIB_CHROMECONF_H_ */ diff --git a/deps/zlib/contrib/bench/zlib_bench.cc b/deps/zlib/contrib/bench/zlib_bench.cc index 5dcdef09f36b76..252560ea9e0b18 100644 --- a/deps/zlib/contrib/bench/zlib_bench.cc +++ b/deps/zlib/contrib/bench/zlib_bench.cc @@ -15,7 +15,7 @@ * Note this code can be compiled outside of the Chromium build system against * the system zlib (-lz) with g++ or clang++ as follows: * - * g++|clang++ -O3 -Wall -std=c++11 -lstdc++ -lz zlib_bench.cc + * g++|clang++ -O3 -Wall -std=c++11 zlib_bench.cc -lstdc++ -lz */ #include @@ -38,7 +38,7 @@ void error_exit(const char* error, int code) { } inline char* string_data(std::string* s) { - return s->empty() ? 0 : &*s->begin(); + return s->empty() ? nullptr : &*s->begin(); } struct Data { @@ -99,10 +99,25 @@ const char* zlib_wrapper_name(zlib_wrapper type) { if (type == kWrapperZRAW) return "RAW"; error_exit("bad wrapper type", int(type)); - return 0; + return nullptr; +} + +static int zlib_strategy = Z_DEFAULT_STRATEGY; + +const char* zlib_level_strategy_name(int compression_level) { + if (compression_level == 0) + return ""; // strategy is meaningless at level 0 + if (zlib_strategy == Z_HUFFMAN_ONLY) + return "huffman "; + if (zlib_strategy == Z_RLE) + return "rle "; + if (zlib_strategy == Z_DEFAULT_STRATEGY) + return ""; + error_exit("bad strategy", zlib_strategy); + return nullptr; } -static int zlib_compression_level; +static int zlib_compression_level = Z_DEFAULT_COMPRESSION; void zlib_compress( const zlib_wrapper type, @@ -119,7 +134,7 @@ void zlib_compress( memset(&stream, 0, sizeof(stream)); int result = deflateInit2(&stream, zlib_compression_level, Z_DEFLATED, - zlib_stream_wrapper_type(type), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + zlib_stream_wrapper_type(type), MAX_MEM_LEVEL, zlib_strategy); if (result != Z_OK) error_exit("deflateInit2 failed", result); @@ -178,14 +193,19 @@ void verify_equal(const char* input, size_t size, std::string* output) { exit(3); } -void zlib_file(const char* name, const zlib_wrapper type) { +void zlib_file(const char* name, const zlib_wrapper type, int width) { /* * Read the file data. */ const auto file = read_file_data_or_exit(name); const int length = static_cast(file.size); const char* data = file.data.get(); - printf("%-40s :\n", name); + + /* + * Report compression strategy and file name. + */ + const char* strategy = zlib_level_strategy_name(zlib_compression_level); + printf("%s%-40s :\n", strategy, name); /* * Chop the data into blocks. @@ -263,9 +283,9 @@ void zlib_file(const char* name, const zlib_wrapper type) { double inflate_rate_max = length * repeats / mega_byte / utime[0]; // type, block size, compression ratio, etc - printf("%s: [b %dM] bytes %6d -> %6u %4.1f%%", - zlib_wrapper_name(type), block_size / (1 << 20), length, - static_cast(output_length), output_length * 100.0 / length); + printf("%s: [b %dM] bytes %*d -> %*u %4.2f%%", + zlib_wrapper_name(type), block_size / (1 << 20), width, length, width, + unsigned(output_length), output_length * 100.0 / length); // compress / uncompress median (max) rates printf(" comp %5.1f (%5.1f) MB/s uncomp %5.1f (%5.1f) MB/s\n", @@ -276,18 +296,24 @@ static int argn = 1; char* get_option(int argc, char* argv[], const char* option) { if (argn < argc) - return !strcmp(argv[argn], option) ? argv[argn++] : 0; - return 0; + return !strcmp(argv[argn], option) ? argv[argn++] : nullptr; + return nullptr; } -bool get_compression(int argc, char* argv[], int* value) { +bool get_compression(int argc, char* argv[], int& value) { if (argn < argc) - *value = atoi(argv[argn++]); - return *value >= 1 && *value <= 9; + value = isdigit(argv[argn][0]) ? atoi(argv[argn++]) : -1; + return value >= 0 && value <= 9; +} + +void get_field_width(int argc, char* argv[], int& value) { + value = atoi(argv[argn++]); } void usage_exit(const char* program) { - printf("usage: %s gzip|zlib|raw [--compression 1:9] files...\n", program); + static auto* options = + "gzip|zlib|raw [--compression 0:9] [--huffman|--rle] [--field width]"; + printf("usage: %s %s files ...\n", program, options); exit(1); } @@ -302,15 +328,30 @@ int main(int argc, char* argv[]) { else usage_exit(argv[0]); - if (!get_option(argc, argv, "--compression")) - zlib_compression_level = Z_DEFAULT_COMPRESSION; - else if (!get_compression(argc, argv, &zlib_compression_level)) - usage_exit(argv[0]); + int file_size_field_width = 0; + + while (argn < argc && argv[argn][0] == '-') { + if (get_option(argc, argv, "--compression")) { + if (!get_compression(argc, argv, zlib_compression_level)) + usage_exit(argv[0]); + } else if (get_option(argc, argv, "--field")) { + get_field_width(argc, argv, file_size_field_width); + } else if (get_option(argc, argv, "--huffman")) { + zlib_strategy = Z_HUFFMAN_ONLY; + } else if (get_option(argc, argv, "--rle")) { + zlib_strategy = Z_RLE; + } else { + usage_exit(argv[0]); + } + } if (argn >= argc) usage_exit(argv[0]); + + if (file_size_field_width < 6) + file_size_field_width = 6; while (argn < argc) - zlib_file(argv[argn++], type); + zlib_file(argv[argn++], type, file_size_field_width); return 0; } diff --git a/deps/zlib/contrib/minizip/iowin32.c b/deps/zlib/contrib/minizip/iowin32.c index 246ceb91a13942..c6bc314b3c28af 100644 --- a/deps/zlib/contrib/minizip/iowin32.c +++ b/deps/zlib/contrib/minizip/iowin32.c @@ -31,14 +31,12 @@ #define _WIN32_WINNT 0x601 #endif -#if _WIN32_WINNT >= _WIN32_WINNT_WIN8 -// see Include/shared/winapifamily.h in the Windows Kit -#if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API))) -#if WINAPI_FAMILY_ONE_PARTITION(WINAPI_FAMILY, WINAPI_PARTITION_APP) +#if !defined(IOWIN32_USING_WINRT_API) +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +// Windows Store or Universal Windows Platform #define IOWIN32_USING_WINRT_API 1 #endif #endif -#endif voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); diff --git a/deps/zlib/contrib/minizip/miniunz.c b/deps/zlib/contrib/minizip/miniunz.c index 3d65401be5cdd7..08737f689a96f3 100644 --- a/deps/zlib/contrib/minizip/miniunz.c +++ b/deps/zlib/contrib/minizip/miniunz.c @@ -12,7 +12,7 @@ Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) */ -#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) && (!defined(__ANDROID_API__)) #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif @@ -27,7 +27,7 @@ #endif #endif -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) @@ -45,6 +45,7 @@ #include #include #include +#include #ifdef _WIN32 # include @@ -97,7 +98,7 @@ void change_file_date(filename,dosdate,tmu_date) SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); CloseHandle(hFile); #else -#ifdef unix || __APPLE__ +#if defined(unix) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) struct utimbuf ut; struct tm newdate; newdate.tm_sec = tmu_date.tm_sec; @@ -125,11 +126,9 @@ int mymkdir(dirname) const char* dirname; { int ret=0; -#ifdef _WIN32 +#if defined(_WIN32) ret = _mkdir(dirname); -#elif unix - ret = mkdir (dirname,0775); -#elif __APPLE__ +#elif defined(unix) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) ret = mkdir (dirname,0775); #endif return ret; diff --git a/deps/zlib/contrib/minizip/minizip.c b/deps/zlib/contrib/minizip/minizip.c index 4288962ecef056..b794953c5c2393 100644 --- a/deps/zlib/contrib/minizip/minizip.c +++ b/deps/zlib/contrib/minizip/minizip.c @@ -12,8 +12,7 @@ Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) */ - -#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) && (!defined(__ANDROID_API__)) #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif @@ -28,7 +27,7 @@ #endif #endif -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) @@ -94,7 +93,7 @@ uLong filetime(f, tmzip, dt) return ret; } #else -#ifdef unix || __APPLE__ +#if defined(unix) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) uLong filetime(f, tmzip, dt) char *f; /* name of file to get info on */ tm_zip *tmzip; /* return value: access, modific. and creation times */ diff --git a/deps/zlib/contrib/minizip/minizip.md b/deps/zlib/contrib/minizip/minizip.md new file mode 100644 index 00000000000000..9f15dd23dec279 --- /dev/null +++ b/deps/zlib/contrib/minizip/minizip.md @@ -0,0 +1,9 @@ +Minizip is a library provided by //third_party/zlib [1]. Its zip and unzip +tools can be built in a developer checkout for testing purposes with: + +```shell + autoninja -C out/Release minizip_bin + autoninja -C out/Release miniunz_bin +``` + +[1] Upstream is https://github.com/madler/zlib/tree/master/contrib/minizip diff --git a/deps/zlib/contrib/optimizations/chunkcopy.h b/deps/zlib/contrib/optimizations/chunkcopy.h index 8bcdadad05a817..9c0b7cb06c2dcc 100644 --- a/deps/zlib/contrib/optimizations/chunkcopy.h +++ b/deps/zlib/contrib/optimizations/chunkcopy.h @@ -29,7 +29,6 @@ #include typedef uint8x16_t z_vec128i_t; #elif defined(INFLATE_CHUNK_SIMD_SSE2) -#pragma GCC target ("sse2") #include typedef __m128i z_vec128i_t; #else @@ -113,6 +112,10 @@ static inline unsigned char FAR* chunkcopy_core_safe( Assert(out + len <= limit, "chunk copy exceeds safety limit"); if ((limit - out) < (ptrdiff_t)CHUNKCOPY_CHUNK_SIZE) { const unsigned char FAR* Z_RESTRICT rfrom = from; + Assert((uintptr_t)out - (uintptr_t)from >= len, + "invalid restrict in chunkcopy_core_safe"); + Assert((uintptr_t)from - (uintptr_t)out >= len, + "invalid restrict in chunkcopy_core_safe"); if (len & 8) { Z_BUILTIN_MEMCPY(out, rfrom, 8); out += 8; @@ -339,6 +342,10 @@ static inline unsigned char FAR* chunkcopy_relaxed( unsigned char FAR* Z_RESTRICT out, const unsigned char FAR* Z_RESTRICT from, unsigned len) { + Assert((uintptr_t)out - (uintptr_t)from >= len, + "invalid restrict in chunkcopy_relaxed"); + Assert((uintptr_t)from - (uintptr_t)out >= len, + "invalid restrict in chunkcopy_relaxed"); return chunkcopy_core(out, from, len); } @@ -361,6 +368,11 @@ static inline unsigned char FAR* chunkcopy_safe( unsigned len, unsigned char FAR* limit) { Assert(out + len <= limit, "chunk copy exceeds safety limit"); + Assert((uintptr_t)out - (uintptr_t)from >= len, + "invalid restrict in chunkcopy_safe"); + Assert((uintptr_t)from - (uintptr_t)out >= len, + "invalid restrict in chunkcopy_safe"); + return chunkcopy_core_safe(out, from, len, limit); } @@ -407,6 +419,26 @@ static inline unsigned char FAR* chunkcopy_lapped_safe( return chunkcopy_lapped_relaxed(out, dist, len); } +/* TODO(cavalcanti): see crbug.com/1110083. */ +static inline unsigned char FAR* chunkcopy_safe_ugly(unsigned char FAR* out, + unsigned dist, + unsigned len, + unsigned char FAR* limit) { +#if defined(__GNUC__) && !defined(__clang__) + /* Speed is the same as using chunkcopy_safe + w/ GCC on ARM (tested gcc 6.3 and 7.5) and avoids + undefined behavior. + */ + return chunkcopy_core_safe(out, out - dist, len, limit); +#elif defined(__clang__) && defined(ARMV8_OS_ANDROID) && !defined(__aarch64__) + /* Seems to perform better on 32bit (i.e. Android). */ + return chunkcopy_core_safe(out, out - dist, len, limit); +#else + /* Seems to perform better on 64bit. */ + return chunkcopy_lapped_safe(out, dist, len, limit); +#endif +} + /* * The chunk-copy code above deals with writing the decoded DEFLATE data to * the output with SIMD methods to increase decode speed. Reading the input diff --git a/deps/zlib/contrib/optimizations/inffast_chunk.c b/deps/zlib/contrib/optimizations/inffast_chunk.c index 4099edf3961d74..8d62920a21185f 100644 --- a/deps/zlib/contrib/optimizations/inffast_chunk.c +++ b/deps/zlib/contrib/optimizations/inffast_chunk.c @@ -95,7 +95,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ - code here; /* retrieved table entry */ + code const *here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ @@ -139,20 +139,20 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ bits += 8; #endif } - here = lcode[hold & lmask]; + here = lcode + (hold & lmask); dolen: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op == 0) { /* literal */ - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ? "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - *out++ = (unsigned char)(here.val); + "inflate: literal 0x%02x\n", here->val)); + *out++ = (unsigned char)(here->val); } else if (op & 16) { /* length base */ - len = (unsigned)(here.val); + len = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { @@ -182,14 +182,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ bits += 8; #endif } - here = dcode[hold & dmask]; + here = dcode + (hold & dmask); dodist: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op & 16) { /* distance base */ - dist = (unsigned)(here.val); + dist = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (bits < op) { #ifdef INFLATE_CHUNK_READ_64LE @@ -276,7 +276,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ the main copy is near the end. */ out = chunkunroll_relaxed(out, &dist, &len); - out = chunkcopy_safe(out, out - dist, len, limit); + out = chunkcopy_safe_ugly(out, dist, len, limit); } else { /* from points to window, so there is no risk of overlapping pointers requiring memset-like behaviour @@ -295,7 +295,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level distance code */ - here = dcode[here.val + (hold & ((1U << op) - 1))]; + here = dcode + here->val + (hold & ((1U << op) - 1)); goto dodist; } else { @@ -305,7 +305,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level length code */ - here = lcode[here.val + (hold & ((1U << op) - 1))]; + here = lcode + here->val + (hold & ((1U << op) - 1)); goto dolen; } else if (op & 32) { /* end-of-block */ diff --git a/deps/zlib/contrib/optimizations/insert_string.h b/deps/zlib/contrib/optimizations/insert_string.h index 69eee3dc9e91fc..9f634ae351b2e5 100644 --- a/deps/zlib/contrib/optimizations/insert_string.h +++ b/deps/zlib/contrib/optimizations/insert_string.h @@ -4,45 +4,51 @@ * Use of this source code is governed by a BSD-style license that can be * found in the Chromium source repository LICENSE file. */ -#ifdef _MSC_VER + +#if defined(_MSC_VER) #define INLINE __inline #else #define INLINE inline #endif -/* Optimized insert_string block */ -#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32) -#define TARGET_CPU_WITH_CRC +#include "cpu_features.h" + // clang-format off #if defined(CRC32_SIMD_SSE42_PCLMUL) - /* Required to make MSVC bot build pass. */ - #include - #if defined(__GNUC__) || defined(__clang__) - #undef TARGET_CPU_WITH_CRC + #include /* Required to make MSVC bot build pass. */ + + #if defined(__clang__) || defined(__GNUC__) #define TARGET_CPU_WITH_CRC __attribute__((target("sse4.2"))) + #else + #define TARGET_CPU_WITH_CRC #endif #define _cpu_crc32_u32 _mm_crc32_u32 #elif defined(CRC32_ARMV8_CRC32) - #include "arm_features.h" #if defined(__clang__) - #undef TARGET_CPU_WITH_CRC #define __crc32cw __builtin_arm_crc32cw + #elif defined(__GNUC__) + #define __crc32cw __builtin_aarch64_crc32cw #endif - #define _cpu_crc32_u32 __crc32cw - - #if defined(__aarch64__) + #if defined(__aarch64__) && defined(__clang__) #define TARGET_CPU_WITH_CRC __attribute__((target("crc"))) - #else // !defined(__aarch64__) + #elif defined(__aarch64__) && defined(__GNUC__) + #define TARGET_CPU_WITH_CRC __attribute__((target("+crc"))) + #elif defined(__clang__) // !defined(__aarch64__) #define TARGET_CPU_WITH_CRC __attribute__((target("armv8-a,crc"))) #endif // defined(__aarch64__) + + #define _cpu_crc32_u32 __crc32cw + #endif // clang-format on + +#if defined(TARGET_CPU_WITH_CRC) + TARGET_CPU_WITH_CRC -local INLINE Pos insert_string_optimized(deflate_state* const s, - const Pos str) { +local INLINE Pos insert_string_simd(deflate_state* const s, const Pos str) { Pos ret; unsigned *ip, val, h = 0; @@ -64,7 +70,8 @@ local INLINE Pos insert_string_optimized(deflate_state* const s, s->prev[str & s->w_mask] = ret; return ret; } -#endif /* Optimized insert_string block */ + +#endif // TARGET_CPU_WITH_CRC /* =========================================================================== * Update a hash value with the given input byte @@ -99,24 +106,22 @@ local INLINE Pos insert_string_c(deflate_state* const s, const Pos str) { } local INLINE Pos insert_string(deflate_state* const s, const Pos str) { -/* String dictionary insertion: faster symbol hashing has a positive impact - * on data compression speeds (around 20% on Intel and 36% on Arm Cortex big - * cores). - * A misfeature is that the generated compressed output will differ from - * vanilla zlib (even though it is still valid 'DEFLATE-d' content). +/* insert_string_simd string dictionary insertion: this SIMD symbol hashing + * significantly improves data compression speed. * - * We offer here a way to disable the optimization if there is the expectation - * that compressed content should match when compared to vanilla zlib. + * Note: the generated compressed output is a valid DEFLATE stream but will + * differ from vanilla zlib output ... */ -#if !defined(CHROMIUM_ZLIB_NO_CASTAGNOLI) - /* TODO(cavalcantii): unify CPU features code. */ -#if defined(CRC32_ARMV8_CRC32) - if (arm_cpu_enable_crc32) - return insert_string_optimized(s, str); -#elif defined(CRC32_SIMD_SSE42_PCLMUL) +#if defined(CHROMIUM_ZLIB_NO_CASTAGNOLI) +/* ... so this build-time option can used to disable the SIMD symbol hasher + * if matching vanilla zlib DEFLATE output is required. + */ (;) /* FALLTHOUGH */ +#elif defined(TARGET_CPU_WITH_CRC) && defined(CRC32_SIMD_SSE42_PCLMUL) if (x86_cpu_enable_simd) - return insert_string_optimized(s, str); -#endif + return insert_string_simd(s, str); +#elif defined(TARGET_CPU_WITH_CRC) && defined(CRC32_ARMV8_CRC32) + if (arm_cpu_enable_crc32) + return insert_string_simd(s, str); #endif return insert_string_c(s, str); } diff --git a/deps/zlib/contrib/tests/DEPS b/deps/zlib/contrib/tests/DEPS new file mode 100644 index 00000000000000..42751740686776 --- /dev/null +++ b/deps/zlib/contrib/tests/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+testing/gtest", + "+base", +] diff --git a/deps/zlib/contrib/tests/OWNERS b/deps/zlib/contrib/tests/OWNERS index 9a2fb6fbdc6b3f..aa6a2d1bb3c041 100644 --- a/deps/zlib/contrib/tests/OWNERS +++ b/deps/zlib/contrib/tests/OWNERS @@ -1 +1,2 @@ cblume@chromium.org +cavalcantii@chromium.org diff --git a/deps/zlib/contrib/tests/fuzzers/BUILD.gn b/deps/zlib/contrib/tests/fuzzers/BUILD.gn index c46b66440073a5..10abe00c0515b6 100644 --- a/deps/zlib/contrib/tests/fuzzers/BUILD.gn +++ b/deps/zlib/contrib/tests/fuzzers/BUILD.gn @@ -9,37 +9,27 @@ group("fuzzers") { } fuzzer_test("zlib_uncompress_fuzzer") { - sources = [ - "uncompress_fuzzer.cc", - ] - deps = [ - "../../../:zlib", - ] + sources = [ "uncompress_fuzzer.cc" ] + deps = [ "../../../:zlib" ] } fuzzer_test("zlib_inflate_fuzzer") { - sources = [ - "inflate_fuzzer.cc", - ] - deps = [ - "../../../:zlib", - ] + sources = [ "inflate_fuzzer.cc" ] + deps = [ "../../../:zlib" ] +} + +fuzzer_test("zlib_streaming_inflate_fuzzer") { + sources = [ "streaming_inflate_fuzzer.cc" ] + deps = [ "../../../:zlib" ] + libfuzzer_options = [ "max_len=256000" ] } fuzzer_test("zlib_deflate_set_dictionary_fuzzer") { - sources = [ - "deflate_set_dictionary_fuzzer.cc", - ] - deps = [ - "../../../:zlib", - ] + sources = [ "deflate_set_dictionary_fuzzer.cc" ] + deps = [ "../../../:zlib" ] } fuzzer_test("zlib_deflate_fuzzer") { - sources = [ - "deflate_fuzzer.cc", - ] - deps = [ - "../../../:zlib", - ] + sources = [ "deflate_fuzzer.cc" ] + deps = [ "../../../:zlib" ] } diff --git a/deps/zlib/contrib/tests/fuzzers/OWNERS b/deps/zlib/contrib/tests/fuzzers/OWNERS index 6397ce69c70649..9a2fb6fbdc6b3f 100644 --- a/deps/zlib/contrib/tests/fuzzers/OWNERS +++ b/deps/zlib/contrib/tests/fuzzers/OWNERS @@ -1,2 +1 @@ cblume@chromium.org -mmoroz@chromium.org diff --git a/deps/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc b/deps/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc index 6098ff162ce1c0..c00e71551d80db 100644 --- a/deps/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc +++ b/deps/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc @@ -2,46 +2,73 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include +#include +#include #include -#include #include #include "third_party/zlib/zlib.h" -static Bytef buffer[256 * 1024] = {0}; +// Fuzzer builds often have NDEBUG set, so roll our own assert macro. +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d Assert failed: %s\n", __FILE__, __LINE__, #cond); \ + exit(1); \ + } \ + } while (0) -// Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - // zlib's deflate requires non-zero input sizes - if (!size) - return 0; - - // We need to strip the 'const' for zlib. - std::vector input_buffer{data, data+size}; - - uLongf buffer_length = static_cast(sizeof(buffer)); + FuzzedDataProvider fdp(data, size); + int level = fdp.PickValueInArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + int windowBits = fdp.PickValueInArray({9, 10, 11, 12, 13, 14, 15}); + int memLevel = fdp.PickValueInArray({1, 2, 3, 4, 5, 6, 7, 8, 9}); + int strategy = fdp.PickValueInArray( + {Z_DEFAULT_STRATEGY, Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED}); + std::vector src = fdp.ConsumeRemainingBytes(); z_stream stream; - stream.next_in = input_buffer.data(); - stream.avail_in = size; - stream.total_in = size; - stream.next_out = buffer; - stream.avail_out = buffer_length; - stream.total_out = buffer_length; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; - if (Z_OK != deflateInit(&stream, Z_DEFAULT_COMPRESSION)) { - deflateEnd(&stream); - assert(false); + // Compress the data one byte at a time to exercise the streaming code. + int ret = + deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + ASSERT(ret == Z_OK); + std::vector compressed(src.size() * 2 + 1000); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + for (uint8_t b : src) { + stream.next_in = &b; + stream.avail_in = 1; + ret = deflate(&stream, Z_NO_FLUSH); + ASSERT(ret == Z_OK); } - - auto deflate_result = deflate(&stream, Z_NO_FLUSH); + stream.next_in = Z_NULL; + stream.avail_in = 0; + ret = deflate(&stream, Z_FINISH); + ASSERT(ret == Z_STREAM_END); + compressed.resize(compressed.size() - stream.avail_out); deflateEnd(&stream); - if (Z_OK != deflate_result) - assert(false); + + // Verify that the data decompresses correctly. + ret = inflateInit2(&stream, windowBits); + ASSERT(ret == Z_OK); + // Make room for at least one byte so it's never empty. + std::vector decompressed(src.size() + 1); + stream.next_in = compressed.data(); + stream.avail_in = compressed.size(); + stream.next_out = decompressed.data(); + stream.avail_out = decompressed.size(); + ret = inflate(&stream, Z_FINISH); + ASSERT(ret == Z_STREAM_END); + decompressed.resize(decompressed.size() - stream.avail_out); + inflateEnd(&stream); + + ASSERT(decompressed == src); return 0; } diff --git a/deps/zlib/contrib/tests/fuzzers/streaming_inflate_fuzzer.cc b/deps/zlib/contrib/tests/fuzzers/streaming_inflate_fuzzer.cc new file mode 100644 index 00000000000000..de4d216756cb42 --- /dev/null +++ b/deps/zlib/contrib/tests/fuzzers/streaming_inflate_fuzzer.cc @@ -0,0 +1,74 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include + +#include "third_party/zlib/zlib.h" + +// Fuzzer builds often have NDEBUG set, so roll our own assert macro. +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d Assert failed: %s\n", __FILE__, __LINE__, #cond); \ + exit(1); \ + } \ + } while (0) + +// Entry point for LibFuzzer. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Deflate data. + z_stream comp_strm; + comp_strm.zalloc = Z_NULL; + comp_strm.zfree = Z_NULL; + comp_strm.opaque = Z_NULL; + int ret = deflateInit(&comp_strm, Z_DEFAULT_COMPRESSION); + ASSERT(ret == Z_OK); + + size_t comp_buf_cap = deflateBound(&comp_strm, size); + uint8_t* comp_buf = (uint8_t*)malloc(comp_buf_cap); + ASSERT(comp_buf != nullptr); + comp_strm.next_out = comp_buf; + comp_strm.avail_out = comp_buf_cap; + comp_strm.next_in = (unsigned char*)data; + comp_strm.avail_in = size; + ret = deflate(&comp_strm, Z_FINISH); + ASSERT(ret == Z_STREAM_END); + size_t comp_sz = comp_buf_cap - comp_strm.avail_out; + + // Inflate comp_buf one chunk at a time. + z_stream decomp_strm; + decomp_strm.zalloc = Z_NULL; + decomp_strm.zfree = Z_NULL; + decomp_strm.opaque = Z_NULL; + ret = inflateInit(&decomp_strm); + ASSERT(ret == Z_OK); + decomp_strm.next_in = comp_buf; + decomp_strm.avail_in = comp_sz; + + while (decomp_strm.avail_in > 0) { + uint8_t decomp_buf[1024]; + decomp_strm.next_out = decomp_buf; + decomp_strm.avail_out = sizeof(decomp_buf); + ret = inflate(&decomp_strm, Z_FINISH); + ASSERT(ret == Z_OK || ret == Z_STREAM_END || ret == Z_BUF_ERROR); + + // Verify the output bytes. + size_t num_out = sizeof(decomp_buf) - decomp_strm.avail_out; + for (size_t i = 0; i < num_out; i++) { + ASSERT(decomp_buf[i] == data[decomp_strm.total_out - num_out + i]); + } + } + + ret = deflateEnd(&comp_strm); + ASSERT(ret == Z_OK); + free(comp_buf); + + inflateEnd(&decomp_strm); + ASSERT(ret == Z_OK); + + return 0; +} diff --git a/deps/zlib/contrib/tests/infcover.cc b/deps/zlib/contrib/tests/infcover.cc new file mode 100644 index 00000000000000..16dd744b47aa9d --- /dev/null +++ b/deps/zlib/contrib/tests/infcover.cc @@ -0,0 +1,686 @@ +/* infcover.c -- test zlib's inflate routines with full code coverage + * Copyright (C) 2011, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* to use, do: ./configure --cover && make cover */ +// clang-format off +#include "infcover.h" +#include +#include +#include + +#include "zlib.h" + +/* get definition of internal structure so we can mess with it (see pull()), + and so we can call inflate_trees() (see cover5()) */ +#define ZLIB_INTERNAL +#include "inftrees.h" +#include "inflate.h" + +/* XXX: use C++ streams instead of printf/fputs/etc due to portability + * as type sizes can vary between platforms. + */ +#include +#define local static + +/* XXX: hacking C assert and plugging into GTest. */ +#include "gtest.h" +#if defined(assert) +#undef assert +#define assert EXPECT_TRUE +#endif + +/* XXX: handle what is a reserved word in C++. */ +#define try try_f + +/* -- memory tracking routines -- */ + +/* + These memory tracking routines are provided to zlib and track all of zlib's + allocations and deallocations, check for LIFO operations, keep a current + and high water mark of total bytes requested, optionally set a limit on the + total memory that can be allocated, and when done check for memory leaks. + + They are used as follows: + + z_stream strm; + mem_setup(&strm) initializes the memory tracking and sets the + zalloc, zfree, and opaque members of strm to use + memory tracking for all zlib operations on strm + mem_limit(&strm, limit) sets a limit on the total bytes requested -- a + request that exceeds this limit will result in an + allocation failure (returns NULL) -- setting the + limit to zero means no limit, which is the default + after mem_setup() + mem_used(&strm, "msg") prints to stderr "msg" and the total bytes used + mem_high(&strm, "msg") prints to stderr "msg" and the high water mark + mem_done(&strm, "msg") ends memory tracking, releases all allocations + for the tracking as well as leaked zlib blocks, if + any. If there was anything unusual, such as leaked + blocks, non-FIFO frees, or frees of addresses not + allocated, then "msg" and information about the + problem is printed to stderr. If everything is + normal, nothing is printed. mem_done resets the + strm members to Z_NULL to use the default memory + allocation routines on the next zlib initialization + using strm. + */ + +/* these items are strung together in a linked list, one for each allocation */ +struct mem_item { + void *ptr; /* pointer to allocated memory */ + size_t size; /* requested size of allocation */ + struct mem_item *next; /* pointer to next item in list, or NULL */ +}; + +/* this structure is at the root of the linked list, and tracks statistics */ +struct mem_zone { + struct mem_item *first; /* pointer to first item in list, or NULL */ + size_t total, highwater; /* total allocations, and largest total */ + size_t limit; /* memory allocation limit, or 0 if no limit */ + int notlifo, rogue; /* counts of non-LIFO frees and rogue frees */ +}; + +/* memory allocation routine to pass to zlib */ +local void *mem_alloc(void *mem, unsigned count, unsigned size) +{ + void *ptr; + struct mem_item *item; + struct mem_zone *zone = static_cast(mem); + size_t len = count * (size_t)size; + + /* induced allocation failure */ + if (zone == NULL || (zone->limit && zone->total + len > zone->limit)) + return NULL; + + /* perform allocation using the standard library, fill memory with a + non-zero value to make sure that the code isn't depending on zeros */ + ptr = malloc(len); + if (ptr == NULL) + return NULL; + memset(ptr, 0xa5, len); + + /* create a new item for the list */ + item = static_cast(malloc(sizeof(struct mem_item))); + if (item == NULL) { + free(ptr); + return NULL; + } + item->ptr = ptr; + item->size = len; + + /* insert item at the beginning of the list */ + item->next = zone->first; + zone->first = item; + + /* update the statistics */ + zone->total += item->size; + if (zone->total > zone->highwater) + zone->highwater = zone->total; + + /* return the allocated memory */ + return ptr; +} + +/* memory free routine to pass to zlib */ +local void mem_free(void *mem, void *ptr) +{ + struct mem_item *item, *next; + struct mem_zone *zone = static_cast(mem); + + /* if no zone, just do a free */ + if (zone == NULL) { + free(ptr); + return; + } + + /* point next to the item that matches ptr, or NULL if not found -- remove + the item from the linked list if found */ + next = zone->first; + if (next) { + if (next->ptr == ptr) + zone->first = next->next; /* first one is it, remove from list */ + else { + do { /* search the linked list */ + item = next; + next = item->next; + } while (next != NULL && next->ptr != ptr); + if (next) { /* if found, remove from linked list */ + item->next = next->next; + zone->notlifo++; /* not a LIFO free */ + } + + } + } + + /* if found, update the statistics and free the item */ + if (next) { + zone->total -= next->size; + free(next); + } + + /* if not found, update the rogue count */ + else + zone->rogue++; + + /* in any case, do the requested free with the standard library function */ + free(ptr); +} + +/* set up a controlled memory allocation space for monitoring, set the stream + parameters to the controlled routines, with opaque pointing to the space */ +local void mem_setup(z_stream *strm) +{ + struct mem_zone *zone; + + zone = static_cast(malloc(sizeof(struct mem_zone))); + assert(zone != NULL); + zone->first = NULL; + zone->total = 0; + zone->highwater = 0; + zone->limit = 0; + zone->notlifo = 0; + zone->rogue = 0; + strm->opaque = zone; + strm->zalloc = mem_alloc; + strm->zfree = mem_free; +} + +/* set a limit on the total memory allocation, or 0 to remove the limit */ +local void mem_limit(z_stream *strm, size_t limit) +{ + struct mem_zone *zone = static_cast(strm->opaque); + + zone->limit = limit; +} + +/* show the current total requested allocations in bytes */ +local void mem_used(z_stream *strm, const char *prefix) +{ + struct mem_zone *zone = static_cast(strm->opaque); + + std::cout << prefix << ": " << zone->total << " allocated" << std::endl; +} + +/* show the high water allocation in bytes */ +local void mem_high(z_stream *strm, const char *prefix) +{ + struct mem_zone *zone = static_cast(strm->opaque); + + std::cout << prefix << ": " << zone->highwater << " high water mark" << std::endl; +} + +/* release the memory allocation zone -- if there are any surprises, notify */ +local void mem_done(z_stream *strm, const char *prefix) +{ + int count = 0; + struct mem_item *item, *next; + struct mem_zone *zone = static_cast(strm->opaque); + + /* show high water mark */ + mem_high(strm, prefix); + + /* free leftover allocations and item structures, if any */ + item = zone->first; + while (item != NULL) { + free(item->ptr); + next = item->next; + free(item); + item = next; + count++; + } + + /* issue alerts about anything unexpected */ + if (count || zone->total) + std::cout << "** " << prefix << ": " + << zone->total << " bytes in " + << count << " blocks not freed" + << std::endl; + + if (zone->notlifo) + std::cout << "** " << prefix << ": " + << zone->notlifo << " frees not LIFO" + << std::endl; + + if (zone->rogue) + std::cout << "** " << prefix << ": " + << zone->rogue << " frees not recognized" + << std::endl; + + /* free the zone and delete from the stream */ + free(zone); + strm->opaque = Z_NULL; + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; +} + +/* -- inflate test routines -- */ + +/* Decode a hexadecimal string, set *len to length, in[] to the bytes. This + decodes liberally, in that hex digits can be adjacent, in which case two in + a row writes a byte. Or they can be delimited by any non-hex character, + where the delimiters are ignored except when a single hex digit is followed + by a delimiter, where that single digit writes a byte. The returned data is + allocated and must eventually be freed. NULL is returned if out of memory. + If the length is not needed, then len can be NULL. */ +local unsigned char *h2b(const char *hex, unsigned *len) +{ + unsigned char *in, *re; + unsigned next, val; + + in = static_cast(malloc((strlen(hex) + 1) >> 1)); + if (in == NULL) + return NULL; + next = 0; + val = 1; + do { + if (*hex >= '0' && *hex <= '9') + val = (val << 4) + *hex - '0'; + else if (*hex >= 'A' && *hex <= 'F') + val = (val << 4) + *hex - 'A' + 10; + else if (*hex >= 'a' && *hex <= 'f') + val = (val << 4) + *hex - 'a' + 10; + else if (val != 1 && val < 32) /* one digit followed by delimiter */ + val += 240; /* make it look like two digits */ + if (val > 255) { /* have two digits */ + in[next++] = val & 0xff; /* save the decoded byte */ + val = 1; /* start over */ + } + } while (*hex++); /* go through the loop with the terminating null */ + if (len != NULL) + *len = next; + re = static_cast(realloc(in, next)); + return re == NULL ? in : re; +} + +/* generic inflate() run, where hex is the hexadecimal input data, what is the + text to include in an error message, step is how much input data to feed + inflate() on each call, or zero to feed it all, win is the window bits + parameter to inflateInit2(), len is the size of the output buffer, and err + is the error code expected from the first inflate() call (the second + inflate() call is expected to return Z_STREAM_END). If win is 47, then + header information is collected with inflateGetHeader(). If a zlib stream + is looking for a dictionary, then an empty dictionary is provided. + inflate() is run until all of the input data is consumed. */ +local void inf(const char *hex, const char *what, unsigned step, int win, unsigned len, + int err) +{ + int ret; + unsigned have; + unsigned char *in, *out; + z_stream strm, copy; + gz_header head; + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, win); + if (ret != Z_OK) { + mem_done(&strm, what); + return; + } + out = static_cast(malloc(len)); assert(out != NULL); + if (win == 47) { + head.extra = out; + head.extra_max = len; + head.name = out; + head.name_max = len; + head.comment = out; + head.comm_max = len; + ret = inflateGetHeader(&strm, &head); assert(ret == Z_OK); + } + in = h2b(hex, &have); assert(in != NULL); + if (step == 0 || step > have) + step = have; + strm.avail_in = step; + have -= step; + strm.next_in = in; + do { + strm.avail_out = len; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); assert(err == 9 || ret == err); + if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_NEED_DICT) + break; + if (ret == Z_NEED_DICT) { + ret = inflateSetDictionary(&strm, in, 1); + assert(ret == Z_DATA_ERROR); + mem_limit(&strm, 1); + ret = inflateSetDictionary(&strm, out, 0); + assert(ret == Z_MEM_ERROR); + mem_limit(&strm, 0); + ((struct inflate_state *)strm.state)->mode = DICT; + ret = inflateSetDictionary(&strm, out, 0); + assert(ret == Z_OK); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_BUF_ERROR); + } + ret = inflateCopy(©, &strm); assert(ret == Z_OK); + ret = inflateEnd(©); assert(ret == Z_OK); + err = 9; /* don't care next time around */ + have += strm.avail_in; + strm.avail_in = step > have ? have : step; + have -= strm.avail_in; + } while (strm.avail_in); + free(in); + free(out); + ret = inflateReset2(&strm, -8); assert(ret == Z_OK); + ret = inflateEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, what); +} + +/* cover all of the lines in inflate.c up to inflate() */ +void cover_support(void) +{ + int ret; + z_stream strm; + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); assert(ret == Z_OK); + mem_used(&strm, "inflate init"); + ret = inflatePrime(&strm, 5, 31); assert(ret == Z_OK); + ret = inflatePrime(&strm, -1, 0); assert(ret == Z_OK); + ret = inflateSetDictionary(&strm, Z_NULL, 0); + assert(ret == Z_STREAM_ERROR); + ret = inflateEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, "prime"); + + inf("63 0", "force window allocation", 0, -15, 1, Z_OK); + inf("63 18 5", "force window replacement", 0, -8, 259, Z_OK); + inf("63 18 68 30 d0 0 0", "force split window update", 4, -8, 259, Z_OK); + inf("3 0", "use fixed blocks", 0, -15, 1, Z_STREAM_END); + inf("", "bad window size", 0, 1, 0, Z_STREAM_ERROR); + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + char versioncpy[] = ZLIB_VERSION; + versioncpy[0] -= 1; + ret = inflateInit_(&strm, versioncpy, (int)sizeof(z_stream)); + assert(ret == Z_VERSION_ERROR); + mem_done(&strm, "wrong version"); + + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); assert(ret == Z_OK); + ret = inflateEnd(&strm); assert(ret == Z_OK); + std::cout << "inflate built-in memory routines" << std::endl;; +} + +/* cover all inflate() header and trailer cases and code after inflate() */ +void cover_wrap(void) +{ + int ret; + z_stream strm, copy; + unsigned char dict[257]; + + ret = inflate(Z_NULL, 0); assert(ret == Z_STREAM_ERROR); + ret = inflateEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); + ret = inflateCopy(Z_NULL, Z_NULL); assert(ret == Z_STREAM_ERROR); + std::cout << "inflate bad parameters" << std::endl; + + inf("1f 8b 0 0", "bad gzip method", 0, 31, 0, Z_DATA_ERROR); + inf("1f 8b 8 80", "bad gzip flags", 0, 31, 0, Z_DATA_ERROR); + inf("77 85", "bad zlib method", 0, 15, 0, Z_DATA_ERROR); + inf("8 99", "set window size from header", 0, 0, 0, Z_OK); + inf("78 9c", "bad zlib window size", 0, 8, 0, Z_DATA_ERROR); + inf("78 9c 63 0 0 0 1 0 1", "check adler32", 0, 15, 1, Z_STREAM_END); + inf("1f 8b 8 1e 0 0 0 0 0 0 1 0 0 0 0 0 0", "bad header crc", 0, 47, 1, + Z_DATA_ERROR); + inf("1f 8b 8 2 0 0 0 0 0 0 1d 26 3 0 0 0 0 0 0 0 0 0", "check gzip length", + 0, 47, 0, Z_STREAM_END); + inf("78 90", "bad zlib header check", 0, 47, 0, Z_DATA_ERROR); + inf("8 b8 0 0 0 1", "need dictionary", 0, 8, 0, Z_NEED_DICT); + inf("78 9c 63 0", "compute adler32", 0, 15, 1, Z_OK); + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, -8); + strm.avail_in = 2; + strm.next_in = (Bytef *)"\x63"; + strm.avail_out = 1; + strm.next_out = (Bytef *)&ret; + mem_limit(&strm, 1); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); + mem_limit(&strm, 0); + memset(dict, 0, 257); + ret = inflateSetDictionary(&strm, dict, 257); + assert(ret == Z_OK); + mem_limit(&strm, (sizeof(struct inflate_state) << 1) + 256); + ret = inflatePrime(&strm, 16, 0); assert(ret == Z_OK); + strm.avail_in = 2; + strm.next_in = (Bytef *)"\x80"; + ret = inflateSync(&strm); assert(ret == Z_DATA_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_STREAM_ERROR); + strm.avail_in = 4; + strm.next_in = (Bytef *)"\0\0\xff\xff"; + ret = inflateSync(&strm); assert(ret == Z_OK); + (void)inflateSyncPoint(&strm); + ret = inflateCopy(©, &strm); assert(ret == Z_MEM_ERROR); + mem_limit(&strm, 0); + ret = inflateUndermine(&strm, 1); assert(ret == Z_DATA_ERROR); + (void)inflateMark(&strm); + ret = inflateEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, "miscellaneous, force memory errors"); +} + +/* input and output functions for inflateBack() */ +local unsigned pull(void *desc, unsigned char **buf) +{ + static unsigned int next = 0; + static unsigned char dat[] = {0x63, 0, 2, 0}; + struct inflate_state *state; + + if (desc == Z_NULL) { + next = 0; + return 0; /* no input (already provided at next_in) */ + } + state = reinterpret_cast(((z_stream *)desc)->state); + if (state != Z_NULL) + state->mode = SYNC; /* force an otherwise impossible situation */ + return next < sizeof(dat) ? (*buf = dat + next++, 1) : 0; +} + +local int push(void *desc, unsigned char *buf, unsigned len) +{ + buf += len; + return desc != Z_NULL; /* force error if desc not null */ +} + +/* cover inflateBack() up to common deflate data cases and after those */ +void cover_back(void) +{ + int ret; + z_stream strm; + unsigned char win[32768]; + + ret = inflateBackInit_(Z_NULL, 0, win, 0, 0); + assert(ret == Z_VERSION_ERROR); + ret = inflateBackInit(Z_NULL, 0, win); assert(ret == Z_STREAM_ERROR); + ret = inflateBack(Z_NULL, Z_NULL, Z_NULL, Z_NULL, Z_NULL); + assert(ret == Z_STREAM_ERROR); + ret = inflateBackEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); + std::cout << "inflateBack bad parameters" << std::endl;; + + mem_setup(&strm); + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + strm.avail_in = 2; + strm.next_in = (Bytef *)"\x03"; + ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); + assert(ret == Z_STREAM_END); + /* force output error */ + strm.avail_in = 3; + strm.next_in = (Bytef *)"\x63\x00"; + ret = inflateBack(&strm, pull, Z_NULL, push, &strm); + assert(ret == Z_BUF_ERROR); + /* force mode error by mucking with state */ + ret = inflateBack(&strm, pull, &strm, push, Z_NULL); + assert(ret == Z_STREAM_ERROR); + ret = inflateBackEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, "inflateBack bad state"); + + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + ret = inflateBackEnd(&strm); assert(ret == Z_OK); + std::cout << "inflateBack built-in memory routines" << std::endl;; +} + +/* do a raw inflate of data in hexadecimal with both inflate and inflateBack */ +local int try(const char *hex, const char *id, int err) +{ + int ret; + unsigned len, size; + unsigned char *in, *out, *win; + char *prefix; + z_stream strm; + + /* convert to hex */ + in = h2b(hex, &len); + assert(in != NULL); + + /* allocate work areas */ + size = len << 3; + out = static_cast(malloc(size)); + assert(out != NULL); + win = static_cast(malloc(32768)); + assert(win != NULL); + prefix = static_cast(malloc(strlen(id) + 6)); + assert(prefix != NULL); + + /* first with inflate */ + strcpy(prefix, id); + strcat(prefix, "-late"); + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, err < 0 ? 47 : -15); + assert(ret == Z_OK); + strm.avail_in = len; + strm.next_in = in; + do { + strm.avail_out = size; + strm.next_out = out; + ret = inflate(&strm, Z_TREES); + assert(ret != Z_STREAM_ERROR && ret != Z_MEM_ERROR); + if (ret == Z_DATA_ERROR || ret == Z_NEED_DICT) + break; + } while (strm.avail_in || strm.avail_out == 0); + if (err) { + assert(ret == Z_DATA_ERROR); + assert(strcmp(id, strm.msg) == 0); + } + inflateEnd(&strm); + mem_done(&strm, prefix); + + /* then with inflateBack */ + if (err >= 0) { + strcpy(prefix, id); + strcat(prefix, "-back"); + mem_setup(&strm); + ret = inflateBackInit(&strm, 15, win); + assert(ret == Z_OK); + strm.avail_in = len; + strm.next_in = in; + ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); + assert(ret != Z_STREAM_ERROR); + if (err) { + assert(ret == Z_DATA_ERROR); + assert(strcmp(id, strm.msg) == 0); + } + inflateBackEnd(&strm); + mem_done(&strm, prefix); + } + + /* clean up */ + free(prefix); + free(win); + free(out); + free(in); + return ret; +} + +/* cover deflate data cases in both inflate() and inflateBack() */ +void cover_inflate(void) +{ + try("0 0 0 0 0", "invalid stored block lengths", 1); + try("3 0", "fixed", 0); + try("6", "invalid block type", 1); + try("1 1 0 fe ff 0", "stored", 0); + try("fc 0 0", "too many length or distance symbols", 1); + try("4 0 fe ff", "invalid code lengths set", 1); + try("4 0 24 49 0", "invalid bit length repeat", 1); + try("4 0 24 e9 ff ff", "invalid bit length repeat", 1); + try("4 0 24 e9 ff 6d", "invalid code -- missing end-of-block", 1); + try("4 80 49 92 24 49 92 24 71 ff ff 93 11 0", + "invalid literal/lengths set", 1); + try("4 80 49 92 24 49 92 24 f b4 ff ff c3 84", "invalid distances set", 1); + try("4 c0 81 8 0 0 0 0 20 7f eb b 0 0", "invalid literal/length code", 1); + try("2 7e ff ff", "invalid distance code", 1); + try("c c0 81 0 0 0 0 0 90 ff 6b 4 0", "invalid distance too far back", 1); + + /* also trailer mismatch just in inflate() */ + try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 1", "incorrect data check", -1); + try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 1", + "incorrect length check", -1); + try("5 c0 21 d 0 0 0 80 b0 fe 6d 2f 91 6c", "pull 17", 0); + try("5 e0 81 91 24 cb b2 2c 49 e2 f 2e 8b 9a 47 56 9f fb fe ec d2 ff 1f", + "long code", 0); + try("ed c0 1 1 0 0 0 40 20 ff 57 1b 42 2c 4f", "length extra", 0); + try("ed cf c1 b1 2c 47 10 c4 30 fa 6f 35 1d 1 82 59 3d fb be 2e 2a fc f c", + "long distance and extra", 0); + try("ed c0 81 0 0 0 0 80 a0 fd a9 17 a9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6", "window end", 0); + inf("2 8 20 80 0 3 0", "inflate_fast TYPE return", 0, -15, 258, + Z_STREAM_END); + inf("63 18 5 40 c 0", "window wrap", 3, -8, 300, Z_OK); +} + +/* XXX(cavalcantii): fix linking error due inflate_table. */ +/* cover remaining lines in inftrees.c */ +/* void cover_trees(void) */ +/* { */ +/* int ret; */ +/* unsigned bits; */ +/* unsigned short lens[16], work[16]; */ +/* code *next, table[ENOUGH_DISTS]; */ + +/* /\* we need to call inflate_table() directly in order to manifest not- */ +/* enough errors, since zlib insures that enough is always enough *\/ */ +/* for (bits = 0; bits < 15; bits++) */ +/* lens[bits] = (unsigned short)(bits + 1); */ +/* lens[15] = 15; */ +/* next = table; */ +/* bits = 15; */ +/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ +/* assert(ret == 1); */ +/* next = table; */ +/* bits = 1; */ +/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ +/* assert(ret == 1); */ +/* fputs("inflate_table not enough errors\n", stderr); */ +/* } */ + +/* cover remaining inffast.c decoding and window copying */ +void cover_fast(void) +{ + inf("e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68" + " ff 7f 0f 0 0 0", "fast length extra bits", 0, -8, 258, Z_DATA_ERROR); + inf("25 fd 81 b5 6d 59 b6 6a 49 ea af 35 6 34 eb 8c b9 f6 b9 1e ef 67 49" + " 50 fe ff ff 3f 0 0", "fast distance extra bits", 0, -8, 258, + Z_DATA_ERROR); + inf("3 7e 0 0 0 0 0", "fast invalid distance code", 0, -8, 258, + Z_DATA_ERROR); + inf("1b 7 0 0 0 0 0", "fast invalid literal/length code", 0, -8, 258, + Z_DATA_ERROR); + inf("d c7 1 ae eb 38 c 4 41 a0 87 72 de df fb 1f b8 36 b1 38 5d ff ff 0", + "fast 2nd level codes and too far back", 0, -8, 258, Z_DATA_ERROR); + inf("63 18 5 8c 10 8 0 0 0 0", "very common case", 0, -8, 259, Z_OK); + inf("63 60 60 18 c9 0 8 18 18 18 26 c0 28 0 29 0 0 0", + "contiguous and wrap around window", 6, -8, 259, Z_OK); + inf("63 0 3 0 0 0 0 0", "copy direct from output", 0, -8, 259, + Z_STREAM_END); +} + +// clang-format on diff --git a/deps/zlib/contrib/tests/infcover.h b/deps/zlib/contrib/tests/infcover.h new file mode 100644 index 00000000000000..b3e112f9154cbe --- /dev/null +++ b/deps/zlib/contrib/tests/infcover.h @@ -0,0 +1,11 @@ +#ifndef __INF_COVER_H__ +#define __INF_COVER_H__ + +void cover_support(void); +void cover_wrap(void); +void cover_back(void); +void cover_inflate(void); +void cover_trees(void); +void cover_fast(void); + +#endif diff --git a/deps/zlib/contrib/tests/run_all_unittests.cc b/deps/zlib/contrib/tests/run_all_unittests.cc new file mode 100644 index 00000000000000..65ed57e7fd7099 --- /dev/null +++ b/deps/zlib/contrib/tests/run_all_unittests.cc @@ -0,0 +1,14 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" + +int main(int argc, char** argv) { + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, argv, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/deps/zlib/contrib/tests/utils_unittest.cc b/deps/zlib/contrib/tests/utils_unittest.cc new file mode 100644 index 00000000000000..52360b03e0fda2 --- /dev/null +++ b/deps/zlib/contrib/tests/utils_unittest.cc @@ -0,0 +1,1013 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the Chromium source repository LICENSE file. + +#include "infcover.h" + +#include +#include + +#include "compression_utils_portable.h" +#include "gtest.h" +#include "zlib.h" + +void TestPayloads(size_t input_size, zlib_internal::WrapperType type) { + std::vector input; + input.reserve(input_size); + for (size_t i = 1; i <= input_size; ++i) + input.push_back(i & 0xff); + + // If it is big enough for GZIP, will work for other wrappers. + std::vector compressed( + zlib_internal::GzipExpectedCompressedSize(input.size())); + std::vector decompressed(input.size()); + + // Libcores's java/util/zip/Deflater default settings: ZLIB, + // DEFAULT_COMPRESSION and DEFAULT_STRATEGY. + unsigned long compressed_size = static_cast(compressed.size()); + int result = zlib_internal::CompressHelper( + type, compressed.data(), &compressed_size, input.data(), input.size(), + Z_DEFAULT_COMPRESSION, nullptr, nullptr); + ASSERT_EQ(result, Z_OK); + + unsigned long decompressed_size = + static_cast(decompressed.size()); + result = zlib_internal::UncompressHelper(type, decompressed.data(), + &decompressed_size, + compressed.data(), compressed_size); + ASSERT_EQ(result, Z_OK); + EXPECT_EQ(input, decompressed); +} + +TEST(ZlibTest, ZlibWrapper) { + // Minimal ZLIB wrapped short stream size is about 8 bytes. + for (size_t i = 1; i < 1024; ++i) + TestPayloads(i, zlib_internal::WrapperType::ZLIB); +} + +TEST(ZlibTest, GzipWrapper) { + // GZIP should be 12 bytes bigger than ZLIB wrapper. + for (size_t i = 1; i < 1024; ++i) + TestPayloads(i, zlib_internal::WrapperType::GZIP); +} + +TEST(ZlibTest, RawWrapper) { + // RAW has no wrapper (V8 Blobs is a known user), size + // should be payload_size + 2 for short payloads. + for (size_t i = 1; i < 1024; ++i) + TestPayloads(i, zlib_internal::WrapperType::ZRAW); +} + +TEST(ZlibTest, InflateCover) { + cover_support(); + cover_wrap(); + cover_back(); + cover_inflate(); + // TODO(cavalcantii): enable this last test. + // cover_trees(); + cover_fast(); +} + +TEST(ZlibTest, DeflateStored) { + const int no_compression = 0; + const zlib_internal::WrapperType type = zlib_internal::WrapperType::GZIP; + std::vector input(1 << 10, 42); + std::vector compressed( + zlib_internal::GzipExpectedCompressedSize(input.size())); + std::vector decompressed(input.size()); + unsigned long compressed_size = static_cast(compressed.size()); + int result = zlib_internal::CompressHelper( + type, compressed.data(), &compressed_size, input.data(), input.size(), + no_compression, nullptr, nullptr); + ASSERT_EQ(result, Z_OK); + + unsigned long decompressed_size = + static_cast(decompressed.size()); + result = zlib_internal::UncompressHelper(type, decompressed.data(), + &decompressed_size, + compressed.data(), compressed_size); + ASSERT_EQ(result, Z_OK); + EXPECT_EQ(input, decompressed); +} + +TEST(ZlibTest, StreamingInflate) { + uint8_t comp_buf[4096], decomp_buf[4096]; + z_stream comp_strm, decomp_strm; + int ret; + + std::vector src; + for (size_t i = 0; i < 1000; i++) { + for (size_t j = 0; j < 40; j++) { + src.push_back(j); + } + } + + // Deflate src into comp_buf. + comp_strm.zalloc = Z_NULL; + comp_strm.zfree = Z_NULL; + comp_strm.opaque = Z_NULL; + ret = deflateInit(&comp_strm, Z_BEST_COMPRESSION); + ASSERT_EQ(ret, Z_OK); + comp_strm.next_out = comp_buf; + comp_strm.avail_out = sizeof(comp_buf); + comp_strm.next_in = src.data(); + comp_strm.avail_in = src.size(); + ret = deflate(&comp_strm, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + size_t comp_sz = sizeof(comp_buf) - comp_strm.avail_out; + + // Inflate comp_buf one 4096-byte buffer at a time. + decomp_strm.zalloc = Z_NULL; + decomp_strm.zfree = Z_NULL; + decomp_strm.opaque = Z_NULL; + ret = inflateInit(&decomp_strm); + ASSERT_EQ(ret, Z_OK); + decomp_strm.next_in = comp_buf; + decomp_strm.avail_in = comp_sz; + + while (decomp_strm.avail_in > 0) { + decomp_strm.next_out = decomp_buf; + decomp_strm.avail_out = sizeof(decomp_buf); + ret = inflate(&decomp_strm, Z_FINISH); + ASSERT_TRUE(ret == Z_OK || ret == Z_STREAM_END || ret == Z_BUF_ERROR); + + // Verify the output bytes. + size_t num_out = sizeof(decomp_buf) - decomp_strm.avail_out; + for (size_t i = 0; i < num_out; i++) { + EXPECT_EQ(decomp_buf[i], src[decomp_strm.total_out - num_out + i]); + } + } + + // Cleanup memory (i.e. makes ASAN bot happy). + ret = deflateEnd(&comp_strm); + EXPECT_EQ(ret, Z_OK); + ret = inflateEnd(&decomp_strm); + EXPECT_EQ(ret, Z_OK); +} + +TEST(ZlibTest, CRCHashBitsCollision) { + // The CRC32c of the hex sequences 2a,14,14,14 and 2a,14,db,14 have the same + // lower 9 bits. Since longest_match doesn't check match[2], a bad match could + // be chosen when the number of hash bits is <= 9. For this reason, the number + // of hash bits must be set higher, regardless of the memlevel parameter, when + // using CRC32c hashing for string matching. See https://crbug.com/1113596 + + std::vector src = { + // Random byte; zlib doesn't match at offset 0. + 123, + + // This will look like 5-byte match. + 0x2a, + 0x14, + 0xdb, + 0x14, + 0x15, + + // Offer a 4-byte match to bump the next expected match length to 5. + 0x2a, + 0x14, + 0x14, + 0x14, + + 0x2a, + 0x14, + 0x14, + 0x14, + 0x15, + }; + + z_stream stream; + stream.zalloc = nullptr; + stream.zfree = nullptr; + + // Using a low memlevel to try to reduce the number of hash bits. Negative + // windowbits means raw deflate, i.e. without the zlib header. + int ret = deflateInit2(&stream, /*comp level*/ 2, /*method*/ Z_DEFLATED, + /*windowbits*/ -15, /*memlevel*/ 2, + /*strategy*/ Z_DEFAULT_STRATEGY); + ASSERT_EQ(ret, Z_OK); + std::vector compressed(100, '\0'); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + stream.next_in = src.data(); + stream.avail_in = src.size(); + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + compressed.resize(compressed.size() - stream.avail_out); + deflateEnd(&stream); + + ret = inflateInit2(&stream, /*windowbits*/ -15); + ASSERT_EQ(ret, Z_OK); + std::vector decompressed(src.size(), '\0'); + stream.next_in = compressed.data(); + stream.avail_in = compressed.size(); + stream.next_out = decompressed.data(); + stream.avail_out = decompressed.size(); + ret = inflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + EXPECT_EQ(0U, stream.avail_out); + inflateEnd(&stream); + + EXPECT_EQ(src, decompressed); +} + +TEST(ZlibTest, CRCHashAssert) { + // The CRC32c of the hex sequences ff,ff,5e,6f and ff,ff,13,ff have the same + // lower 15 bits. This means longest_match's assert that match[2] == scan[2] + // won't hold. However, such hash collisions are only possible when one of the + // other four bytes also mismatch. This tests that zlib's assert handles this + // case. + + std::vector src = { + // Random byte; zlib doesn't match at offset 0. + 123, + + // This has the same hash as the last byte sequence, and the first two and + // last two bytes match; though the third and the fourth don't. + 0xff, + 0xff, + 0x5e, + 0x6f, + 0x12, + 0x34, + + // Offer a 5-byte match to bump the next expected match length to 6 + // (because the two first and two last bytes need to match). + 0xff, + 0xff, + 0x13, + 0xff, + 0x12, + + 0xff, + 0xff, + 0x13, + 0xff, + 0x12, + 0x34, + }; + + z_stream stream; + stream.zalloc = nullptr; + stream.zfree = nullptr; + + int ret = deflateInit2(&stream, /*comp level*/ 5, /*method*/ Z_DEFLATED, + /*windowbits*/ -15, /*memlevel*/ 8, + /*strategy*/ Z_DEFAULT_STRATEGY); + ASSERT_EQ(ret, Z_OK); + std::vector compressed(100, '\0'); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + stream.next_in = src.data(); + stream.avail_in = src.size(); + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + compressed.resize(compressed.size() - stream.avail_out); + deflateEnd(&stream); + + ret = inflateInit2(&stream, /*windowbits*/ -15); + ASSERT_EQ(ret, Z_OK); + std::vector decompressed(src.size(), '\0'); + stream.next_in = compressed.data(); + stream.avail_in = compressed.size(); + stream.next_out = decompressed.data(); + stream.avail_out = decompressed.size(); + ret = inflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + EXPECT_EQ(0U, stream.avail_out); + inflateEnd(&stream); + + EXPECT_EQ(src, decompressed); +} + +// Fuzzer generated. +static const uint8_t checkMatchCrashData[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, + 0x6e, 0x6e, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x01, 0x39, 0x6e, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x6e, + 0x00, 0x00, 0x0a, 0x9a, 0x00, 0x00, 0x6e, 0x6e, 0x6e, 0x2a, 0x00, 0x00, + 0x00, 0xd5, 0xf0, 0x00, 0x81, 0x02, 0xf3, 0xfd, 0xff, 0xab, 0xf3, 0x6e, + 0x7e, 0x04, 0x5b, 0xf6, 0x2a, 0x2c, 0xf8, 0x00, 0x54, 0xf3, 0xa5, 0x0e, + 0xfd, 0x6e, 0xff, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xa4, 0x0b, 0xa5, 0x2a, 0x0d, 0x10, 0x01, 0x26, 0xf6, 0x04, 0x0e, + 0xff, 0x6e, 0x6e, 0x6e, 0x76, 0x00, 0x00, 0x87, 0x01, 0xfe, 0x0d, 0xb6, + 0x6e, 0x6e, 0xf7, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfd, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x9b, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x6e, 0xff, 0xff, 0x00, + 0x00, 0xd5, 0xf0, 0x00, 0xff, 0x40, 0x7e, 0x0b, 0xa5, 0x10, 0x67, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x40, 0x7e, 0x0b, 0xa5, 0x10, 0x67, + 0x7e, 0x32, 0x6e, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x40, 0x0b, 0xa5, + 0x10, 0x67, 0x01, 0xfe, 0x0d, 0xb6, 0x2a, 0x00, 0x00, 0x58, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3d, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd6, 0x2d, 0x2d, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x66, 0x8a, 0x8a, 0x8a, 0xee, 0x1d, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0xee, 0x0a, 0x00, 0x00, 0x00, 0x54, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf3, 0x00, 0x00, 0xff, 0xff, 0x23, 0x7e, 0x00, 0x1e, + 0x00, 0x00, 0xd5, 0xf0, 0x00, 0xff, 0x40, 0x0b, 0xa5, 0x10, 0x67, 0x01, + 0xfe, 0x0d, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x2d, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x0e, + 0xfb, 0x00, 0x10, 0x24, 0x00, 0x00, 0xfb, 0xff, 0x00, 0x00, 0xff, 0x1f, + 0xb3, 0x00, 0x04, 0x3d, 0x00, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x01, 0x45, 0x3d, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x11, 0x21, 0x00, 0x1e, + 0x00, 0x0c, 0xb3, 0xfe, 0x0e, 0xee, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6e, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x6e, 0x00, + 0x00, 0x87, 0x00, 0x33, 0x38, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0xff, 0xff, 0xff, 0x04, 0x3f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xff, 0x00, 0x31, 0x13, 0x13, 0x13, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x30, 0x83, 0x33, + 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0xff, 0xff, 0x7d, 0xff, 0x00, 0x01, + 0x10, 0x0d, 0x2a, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x11, + 0x21, 0x00, 0xa5, 0x00, 0x68, 0x68, 0x68, 0x67, 0x00, 0x00, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x68, 0x68, 0x68, 0x68, 0x00, 0x00, 0xfa, 0xff, 0xff, + 0x03, 0x01, 0xff, 0x02, 0x00, 0x00, 0x68, 0x68, 0x68, 0x68, 0x0a, 0x10, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x06, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfa, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0x00, 0x06, 0x04, + 0x00, 0xf8, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x06, 0x04, 0x6e, + 0x7e, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x6e, 0x6e, 0x6e, + 0x00, 0x01, 0x38, 0xd5, 0xf0, 0x00, 0x00, 0x2a, 0xfe, 0x04, 0x5b, 0x0d, + 0xfd, 0x6e, 0x92, 0x28, 0xf9, 0xfb, 0xff, 0x07, 0xd2, 0xd6, 0x2d, 0x2d, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0xc2, 0x91, 0x00, 0x5b, 0xef, 0xde, 0xf2, 0x6e, 0x6e, 0xfd, + 0x0c, 0x02, 0x91, 0x62, 0x91, 0xfd, 0x6e, 0x6e, 0xd3, 0x06, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, + 0xd5, 0xf0, 0x00, 0xff, 0x00, 0x00, 0x31, 0x13, 0x13, 0x13, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x13, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x6e, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x09, 0x00, 0x6a, 0x24, 0x26, 0x30, 0x01, 0x2e, 0x2a, 0xfe, + 0x04, 0x5b, 0x0d, 0xfd, 0x6e, 0x6e, 0xd7, 0x06, 0x6e, 0x6e, 0x6e, 0x00, + 0x00, 0xb1, 0xb1, 0xb1, 0xb1, 0x00, 0x00, 0x00, 0x6e, 0x5b, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x24, 0x2a, 0x6e, 0x5c, 0x24, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x05, 0x00, 0x00, 0x00, 0x5d, 0x10, 0x6e, 0x6e, 0xa5, 0x2f, 0x00, 0x00, + 0x95, 0x87, 0x00, 0x6e}; + +TEST(ZlibTest, CheckMatchCrash) { + // See https://crbug.com/1113142. + z_stream stream; + stream.zalloc = nullptr; + stream.zfree = nullptr; + + // Low windowbits to hit window sliding also with a relatively small input. + int ret = deflateInit2(&stream, /*comp level*/ 5, /*method*/ Z_DEFLATED, + /*windowbits*/ -9, /*memlevel*/ 8, + /*strategy*/ Z_DEFAULT_STRATEGY); + ASSERT_EQ(ret, Z_OK); + + uint8_t compressed[sizeof(checkMatchCrashData) * 2]; + stream.next_out = compressed; + stream.avail_out = sizeof(compressed); + + for (size_t i = 0; i < sizeof(checkMatchCrashData); i++) { + ASSERT_GT(stream.avail_out, 0U); + stream.next_in = (uint8_t*)&checkMatchCrashData[i]; + stream.avail_in = 1; + ret = deflate(&stream, Z_NO_FLUSH); + ASSERT_EQ(ret, Z_OK); + } + + stream.next_in = nullptr; + stream.avail_in = 0; + ASSERT_GT(stream.avail_out, 0U); + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + size_t compressed_sz = sizeof(compressed) - stream.avail_out; + deflateEnd(&stream); + + uint8_t decompressed[sizeof(checkMatchCrashData)]; + ret = inflateInit2(&stream, -15); + ASSERT_EQ(ret, Z_OK); + stream.next_in = compressed; + stream.avail_in = compressed_sz; + stream.next_out = decompressed; + stream.avail_out = sizeof(decompressed); + ret = inflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + inflateEnd(&stream); + ASSERT_EQ( + memcmp(checkMatchCrashData, decompressed, sizeof(checkMatchCrashData)), + 0); +} + +TEST(ZlibTest, DeflateRLEUninitUse) { + // MSan would complain about use of uninitialized values in deflate_rle if the + // window isn't zero-initialized. See crbug.com/1137613. Similar problems + // exist in other places in zlib, e.g. longest_match (crbug.com/1144420) but + // we don't have as nice test cases. + + int level = 9; + int windowBits = 9; + int memLevel = 8; + int strategy = Z_RLE; + const std::vector src{ + 0x31, 0x64, 0x38, 0x32, 0x30, 0x32, 0x30, 0x36, 0x65, 0x35, 0x38, 0x35, + 0x32, 0x61, 0x30, 0x36, 0x65, 0x35, 0x32, 0x66, 0x30, 0x34, 0x38, 0x37, + 0x61, 0x31, 0x38, 0x36, 0x37, 0x37, 0x31, 0x39, 0x0a, 0x65, 0x62, 0x00, + 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, + 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, + 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0x95, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0e, 0x0a, 0x54, 0x52, + 0x58, 0x56, 0xab, 0x26, 0x13, 0x53, 0x5a, 0xb5, 0x30, 0xbb, 0x96, 0x44, + 0x80, 0xe6, 0xc5, 0x0a, 0xd0, 0x47, 0x7a, 0xa0, 0x4e, 0xbe, 0x30, 0xdc, + 0xa1, 0x08, 0x54, 0xe1, 0x51, 0xd1, 0xea, 0xef, 0xdb, 0xa1, 0x2d, 0xb4, + 0xb9, 0x58, 0xb1, 0x2f, 0xf0, 0xae, 0xbc, 0x07, 0xd1, 0xba, 0x7f, 0x14, + 0xa4, 0xde, 0x99, 0x7f, 0x4d, 0x3e, 0x25, 0xd9, 0xef, 0xee, 0x4f, 0x38, + 0x7b, 0xaf, 0x3f, 0x6b, 0x53, 0x5a, 0xcb, 0x1f, 0x97, 0xb5, 0x43, 0xa3, + 0xe8, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, + 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, + 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, + 0x9f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3c, + 0x73, 0x70, 0x23, 0x87, 0xec, 0xf8, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc1, 0x00, 0x00, 0x9f, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, + 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + }; + + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + + // Compress the data one byte at a time to exercise the streaming code. + int ret = + deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + ASSERT_EQ(ret, Z_OK); + std::vector compressed(src.size() * 2 + 1000); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + for (uint8_t b : src) { + stream.next_in = &b; + stream.avail_in = 1; + ret = deflate(&stream, Z_NO_FLUSH); + ASSERT_EQ(ret, Z_OK); + } + stream.next_in = Z_NULL; + stream.avail_in = 0; + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + deflateEnd(&stream); +} + +static const char zFixedCorruptionData[] = + "AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYA" + "AZABBABCABDABEABFABGABHABIABJABKABLABMABNABOABPABQABRABSABTABUABVABWABXABY" + "ABZACBACCACDACEACFACGACHACIACJACKACLACMACNACOACPACQACRACSACTACUACVACWACXAC" + "YACZADBADCADDADEADFADGADHADIADJADKADLADMADNADOADPADQADRADSADTADUADVADWADXA" + "DYADZAEBAECAEDAEEAEFAEGAEHAEIAEJAEKAELAEMAENAEOAEPAEQAERAESAETAEUAEVAEWAEX" + "AEYAEZAFBAFCAFDAFEAFFAFGAFHAFIAFJAFKAFLAFMAFNAFOAFPAFQAFRAFSAFTAFUAFVAFWAF" + "XAFYAFZAGBAGCAGDAGEAGFAGGAGHAGIAGJAGKAGLAGMAGNAGOAGPAGQAGRAGSAGTAGUAGVAGWA" + "GXAGYAGZAHBAHCAHDAHEAHFAHGAHHAHIAHJAHKAHLAHMAHNAHOAHPAHQAHRAHSAHTAHUAHVAHW" + "AHXAHYAHZAIBAICAIDAIEAIFAIGAIHAIIAIJAIKAILAIMAINAIOAIPAIQAIRAISAITAIUAIVAI" + "WAIXAIYAIZAJBAJCAJDAJEAJFAJGAJHAJIAJJAJKAJLAJMAJNAJOAJPAJQAJRAJSAJTAJUAJVA" + "JWAJXAJYAJZAKBAKCAKDAKEAKFAKGAKHAKIAKJAKKAKLAKMAKNAKOAKPAKQAKRAKSAKTAKUAKV" + "AKWAKXAKYAKZALBALCALDALEALFALGALHALIALJALKALLALMALNALOALPALQALRALSALTALUAL" + "VALWALXALYALZAMBAMCAMDAMEAMFAMGAMHAMIAMJAMKAMLAMMAMNAMOAMPAMQAMRAMSAMTAMUA" + "MVAMWAMXAMYAMZANBANCANDANEANFANGANHANIANJANKANLANMANNANOANPANQANRANSANTANU" + "ANVANWANXANYANZAOBAOCAODAOEAOFAOGAOHAOIAOJAOKAOLAOMAONAOOAOPAOQAORAOSAOTAO" + "UAOVAOWAOXAOYAOZAPBAPCAPDAPEAPFAPGAPHAPIAPJAPKAPLAPMAPNAPOAPPAPQAPRAPSAPTA" + "PUAPVAPWAPXAPYAPZAQBAQCAQDAQEAQFAQGAQHAQIAQJAQKAQLAQMAQNAQOAQPAQQAQRAQSAQT" + "AQUAQVAQWAQXAQYAQZARBARCARDAREARFARGARHARIARJARKARLARMARNAROARPARQARRARSAR" + "TARUARVARWARXARYARZASBASCASDASEASFASGASHASIASJASKASLASMASNASOASPASQASRASSA" + "STASUASVASWASXASYASZATBATCATDATEATFATGATHATIATJATKATLATMATNATOATPATQATRATS" + "ATTATUATVATWATXATYATZAUBAUCAUDAUEAUFAUGAUHAUIAUJAUKAULAUMAUNAUOAUPAUQAURAU" + "SAUTAUUAUVAUWAUXAUYAUZAVBAVCAVDAVEAVFAVGAVHAVIAVJAVKAVLAVMAVNAVOAVPAVQAVRA" + "VSAVTAVUAVVAVWAVXAVYAVZAWBAWCAWDAWEAWFAWGAWHAWIAWJAWKAWLAWMAWNAWOAWPAWQAWR" + "AWSAWTAWUAWVAWWAWXAWYAWZAXBAXCAXDAXEAXFAXGAXHAXIAXJAXKAXLAXMAXNAXOAXPAXQAX" + "RAXSAXTAXUAXVAXWAXXAXYAXZAYBAYCAYDAYEAYFAYGAYHAYIAYJAYKAYLAYMAYNAYOAYPAYQA" + "YRAYSAYTAYUAYVAYWAYXAYYAYZAZBAZCAZDAZEAZFAZGAZHAZIAZJAZKAZLAZMAZNAZOAZPAZQ" + "AZRAZSAZTAZUAZVAZWAZXAZYAZZBBBCBBDBBEBBFBBGBBHBBIBBJBBKBBLBBMBBNBBOBBPBBQB" + "BRBBSBBTBBUBBVBBWBBXBBYBBZBCCBCDBCEBCFBCGBCHBCIBCJBCKBCLBCMBCNBCOBCPBCQBCR" + "BCSBCTBCUBCVBCWBCXBCYBCZBDCBDDBDEBDFBDGBDHBDIBDJBDKBDLBDMBDNBDOBDPBDQBDRBD" + "SBDTBDUBDVBDWBDXBDYBDZBECBEDBEEBEFBEGBEHBEIBEJBEKBELBEMBENBEOBEPBEQBERBESB" + "ETBEUBEVBEWBEXBEYBEZBFCBFDBFEBFFBFGBFHBFIBFJBFKBFLBFMBFNBFOBFPBFQBFRBFSBFT" + "BFUBFVBFWBFXBFYBFZBGCBGDBGEBGFBGGBGHBGIBGJBGKBGLBGMBGNBGOBGPBGQBGRBGSBGTBG" + "UBGVBGWBGXBGYBGZBHCBHDBHEBHFBHGBHHBHIBHJBHKBHLBHMBHNBHOBHPBHQBHRBHSBHTBHUB" + "HVBHWBHXBHYBHZBICBIDBIEBIFBIGBIHBIIBIJBIKBILBIMBINBIOBIPBIQBIRBISBITBIUBIV" + "BIWBIXBIYBIZBJCBJDBJEBJFBJGBJHBJIBJJBJKBJLBJMBJNBJOBJPBJQBJRBJSBJTBJUBJVBJ" + "WBJXBJYBJZBKCBKDBKEBKFBKGBKHBKIBKJBKKBKLBKMBKNBKOBKPBKQBKRBKSBKTBKUBKVBKWB" + "KXBKYBKZBLCBLDBLEBLFBLGBLHBLIBLJBLKBLLBLMBLNBLOBLPBLQBLRBLSBLTBLUBLVBLWBLX" + "BLYBLZBMCBMDBMEBMFBMGBMHBMIBMJBMKBMLBMMBMNBMOBMPBMQBMRBMSBMTBMUBMVBMWBMXBM" + "YBMZBNCBNDBNEBNFBNGBNHBNIBNJBNKBNLBNMBNNBNOBNPBNQBNRBNSBNTBNUBNVBNWBNXBNYB" + "NZBOCBODBOEBOFBOGBOHBOIBOJBOKBOLBOMBONBOOBOPBOQBORBOSBOTBOUBOVBOWBOXBOYBOZ" + "BPCBPDBPEBPFBPGBPHBPIBPJBPKBPLBPMBPNBPOBPPBPQBPRBPSBPTBPUBPVBPWBPXBPYBPZBQ" + "CBQDBQEBQFBQGBQHBQIBQJBQKBQLBQMBQNBQOBQPBQQBQRBQSBQTBQUBQVBQWBQXBQYBQZBRCB" + "RDBREBRFBRGBRHBRIBRJBRKBRLBRMBRNBROBRPBRQBRRBRSBRTBRUBRVBRWBRXBRYBRZBSCBSD" + "BSEBSFBSGBSHBSIBSJBSKBSLBSMBSNBSOBSPBSQBSRBSSBSTBSUBSVBSWBSXBSYBSZBTCBTDBT" + "EBTFBTGBTHBTIBTJBTKBTLBTMBTNBTOBTPBTQBTRBTSBTTBTUBTVBTWBTXBTYBTZBUCBUDBUEB" + "UFBUGBUHBUIBUJBUKBULBUMBUNBUOBUPBUQBURBUSBUTBUUBUVBUWBUXBUYBUZBVCBVDBVEBVF" + "BVGBVHBVIBVJBVKBVLBVMBVNBVOBVPBVQBVRBVSBVTBVUBVVBVWBVXBVYBVZBWCBWDBWEBWFBW" + "GBWHBWIBWJBWKBWLBWMBWNBWOBWPBWQBWRBWSBWTBWUBWVBWWBWXBWYBWZBXCBXDBXEBXFBXGB" + "XHBXIBXJBXKBXLBXMBXNBXOBXPBXQBXRBXSBXTBXUBXVBXWBXXBXYBXZBYCBYDBYEBYFBYGBYH" + "BYIBYJBYKBYLBYMBYNBYOBYPBYQBYRBYSBYTBYUBYVBYWBYXBYYBYZBZCBZDBZEBZFBZGBZHBZ" + "IBZJBZKBZLBZMBZNBZOBZPBZQBZRBZSBZTBZUBZVBZWBZXBZYBZZCCCDCCECCFCCGCCHCCICCJ" + "CCKCCLCCMCCNCCOCCPCCQCCRCCSCCTCCUCCVCCWCCXCCYCCZCDDCDECDFCDGCDHCDICDJCDKCD" + "LCDMCDNCDOCDPCDQCDRCDSCDTCDUCDVCDWCDXCDYCDZCEDCEECEFCEGCEHCEICEJCEKCELCEMC" + "ENCEOCEPCEQCERCESCETCEUCEVCEWCEXCEYCEZCFDCFECFFCFGCFHCFICFJCFKCFLCFMCFNCFO" + "CFPCFQCFRCFSCFTCFUCFVCFWCFXCFYCFZCGDCGECGFCGGCGHCGICGJCGKCGLCGMCGNCGOCGPCG" + "QCGRCGSCGTCGUCGVCGWCGXCGYCGZCHDCHECHFCHGCHHCHICHJCHKCHLCHMCHNCHOCHPCHQCHRC" + "HSCHTCHUCHVCHWCHXCHYCHZCIDCIECIFCIGCIHCIICIJCIKCILCIMCINCIOCIPCIQCIRCISCIT" + "CIUCIVCIWCIXCIYCIZCJDCJECJFCJGCJHCJICJJCJKCJLCJMCJNCJOCJPCJQCJRCJSCJTCJUCJ" + "VCJWCJXCJYCJZCKDCKECKFCKGCKHCKICKJCKKCKLCKMCKNCKOCKPCKQCKRCKSCKTCKUCKVCKWC" + "KXCKYCKZCLDCLECLFCLGCLHCLICLJCLKCLLCLMCLNCLOCLPCLQCLRCLSCLTCLUCLVCLWCLXCLY" + "CLZCMDCMECMFCMGCMHCMICMJCMKCMLCMMCMNCMOCMPCMQCMRCMSCMTCMUCMVCMWCMXCMYCMZCN" + "DCNECNFCNGCNHCNICNJCNKCNLCNMCNNCNOCNPCNQCNRCNSCNTCNUCNVCNWCNXCNYCNZCODCOEC" + "OFCOGCOHCOICOJCOKCOLCOMCONCOOCOPCOQCORCOSCOTCOUCOVCOWCOXCOYCOZCPDCPECPFCPG" + "CPHCPICPJCPKCPLCPMCPNCPOCPPCPQCPRCPSCPTCPUCPVCPWCPXCPYCPZCQDCQECQFCQGCQHCQ" + "ICQJCQKCQLCQMCQNCQOCQPCQQCQRCQSCQTCQUCQVCQWCQXCQYCQZCRDCRECRFCRGCRHCRICRJC" + "RKCRLCRMCRNCROCRPCRQCRRCRSCRTCRUCRVCRWCRXCRYCRZCSDCSECSFCSGCSHCSICSJCSKCSL" + "CSMCSNCSOCSPCSQCSRCSSCSTCSUCSVCSWCSXCSYCSZCTDCTECTFCTGCTHCTICTJCTKCTLCTMCT" + "NCTOCTPCTQCTRCTSCTTCTUCTVCTWCTXCTYCTZCUDCUECUFCUGCUHCUICUJCUKCULCUMCUNCUOC" + "UPCUQCURCUSCUTCUUCUVCUWCUXCUYCUZCVDCVECVFCVGCVHCVICVJCVKCVLCVMCVNCVOCVPCVQ" + "CVRCVSCVTCVUCVVCVWCVXCVYCVZCWDCWECWFCWGCWHCWICWJCWKCWLCWMCWNCWOCWPCWQCWRCW" + "SCWTCWUCWVCWWCWXCWYCWZCXDCXECXFCXGCXHCXICXJCXKCXLCXMCXNCXOCXPCXQCXRCXSCXTC" + "XUCXVCXWCXXCXYCXZCYDCYECYFCYGCYHCYICYJCYKCYLCYMCYNCYOCYPCYQCYRCYSCYTCYUCYV" + "CYWCYXCYYCYZCZDCZECZFCZGCZHCZICZJCZKCZLCZMCZNCZOCZPCZQCZRCZSCZTCZUCZVCZWCZ" + "XCZYCZZDDDEDDFDDGDDHDDIDDJDDKDDLDDMDDNDDODDPDDQDDRDDSDDTDDUDDVDDWDDXDDYDDZ" + "DEEDEFDEGDEHDEIDEJDEKDELDEMDENDEODEPDEQDERDESDETDEUDEVDEWDEXDEYDEZDFEDFFDF" + "GDFHDFIDFJDFKDFLDFMDFNDFODFPDFQDFRDFSDFTDFUDFVDFWDFXDFYDFZDGEDGFDGGDGHDGID" + "GJDGKDGLDGMDGNDGODGPDGQDGRDGSDGTDGUDGVDGWDGXDGYDGZDHEDHFDHGDHHDHIDHJDHKDHL" + "DHMDHNDHODHPDHQDHRDHSDHTDHUDHVDHWDHXDHYDHZDIEDIFDIGDIHDIIDIJDIKDILDIMDINDI" + "ODIPDIQDIRDISDITDIUDIVDIWDIXDIYDIZDJEDJFDJGDJHDJIDJJDJKDJLDJMDJNDJODJPDJQD" + "JRDJSDJTDJUDJVDJWDJXDJYDJZDKEDKFDKGDKHDKIDKJDKKDKLDKMDKNDKODKPDKQDKRDKSDKT" + "DKUDKVDKWDKXDKYDKZDLEDLFDLGDLHDLIDLJDLKDLLDLMDLNDLODLPDLQDLRDLSDLTDLUDLVDL" + "WDLXDLYDLZDMEDMFDMGDMHDMIDMJDMKDMLDMMDMNDMODMPDMQDMRDMSDMTDMUDMVDMWDMXDMYD" + "MZDNEDNFDNGDNHDNIDNJDNKDNLDNMDNNDNODNPDNQDNRDNSDNTDNUDNVDNWDNXDNYDNZDOEDOF" + "DOGDOHDOIDOJDOKDOLDOMDONDOODOPDOQDORDOSDOTDOUDOVDOWDOXDOYDOZDPEDPFDPGDPHDP" + "IDPJDPKDPLDPMDPNDPODPPDPQDPRDPSDPTDPUDPVDPWDPXDPYDPZDQEDQFDQGDQHDQIDQJDQKD" + "QLDQMDQNDQODQPDQQDQRDQSDQTDQUDQVDQWDQXDQYDQZDREDRFDRGDRHDRIDRJDRKDRLDRMDRN" + "DRODRPDRQDRRDRSDRTDRUDRVDRWDRXDRYDRZDSEDSFDSGDSHDSIDSJDSKDSLDSMDSNDSODSPDS" + "QDSRDSSDSTDSUDSVDSWDSXDSYDSZDTEDTFDTGDTHDTIDTJDTKDTLDTMDTNDTODTPDTQDTRDTSD" + "TTDTUDTVDTWDTXDTYDTZDUEDUFDUGDUHDUIDUJDUKDULDUMDUNDUODUPDUQDURDUSDUTDUUDUV" + "DUWDUXDUYDUZDVEDVFDVGDVHDVIDVJDVKDVLDVMDVNDVODVPDVQDVRDVSDVTDVUDVVDVWDVXDV" + "YDVZDWEDWFDWGDWHDWIDWJDWKDWLDWMDWNDWODWPDWQDWRDWSDWTDWUDWVDWWDWXDWYDWZDXED" + "XFDXGDXHDXIDXJDXKDXLDXMDXNDXODXPDXQDXRDXSDXTDXUDXVDXWDXXDXYDXZDYEDYFDYGDYH" + "DYIDYJDYKDYLDYMDYNDYODYPDYQDYRDYSDYTDYUDYVDYWDYXDYYDYZDZEDZFDZGDZHDZIDZJDZ" + "KDZLDZMDZNDZODZPDZQDZRDZSDZTDZUDZVDZWDZXDZYDZZEEEFEEGEEHEEIEEJEEKEELEEMEEN" + "EEOEEPEEQEEREESEETEEUEEVEEWEEXEEYEEZEFFEFGEFHEFIEFJEFKEFLEFMEFNEFOEFPEFQEF" + "REFSEFTEFUEFVEFWEFXEFYEFZEGFEGGEGHEGIEGJEGKEGLEGMEGNEGOEGPEGQEGREGSEGTEGUE" + "GVEGWEGXEGYEGZEHFEHGEHHEHIEHJEHKEHLEHMEHNEHOEHPEHQEHREHSEHTEHUEHVEHWEHXEHY" + "EHZEIFEIGEIHEIIEIJEIKEILEIMEINEIOEIPEIQEIREISEITEIUEIVEIWEIXEIYEIZEJFEJGEJ" + "HEJIEJJEJKEJLEJMEJNEJOEJPEJQEJREJSEJTEJUEJVEJWEJXEJYEJZEKFEKGEKHEKIEKJEKKE" + "KLEKMEKNEKOEKPEKQEKREKSEKTEKUEKVEKWEKXEKYEKZELFELGELHELIELJELKELLELMELNELO" + "ELPELQELRELSELTELUELVELWELXELYELZEMFEMGEMHEMIEMJEMKEMLEMMEMNEMOEMPEMQEMREM" + "SEMTEMUEMVEMWEMXEMYEMZENFENGENHENIENJENKENLENMENNENOENPENQENRENSENTENUENVE" + "NWENXENYENZEOFEOGEOHEOIEOJEOKEOLEOMEONEOOEOPEOQEOREOSEOTEOUEOVEOWEOXEOYEOZ" + "EPFEPGEPHEPIEPJEPKEPLEPMEPNEPOEPPEPQEPREPSEPTEPUEPVEPWEPXEPYEPZEQFEQGEQHEQ" + "IEQJEQKEQLEQMEQNEQOEQPEQQEQREQSEQTEQUEQVEQWEQXEQYEQZERFERGERHERIERJERKERLE" + "RMERNEROERPERQERRERSERTERUERVERWERXERYERZESFESGESHESIESJESKESLESMESNESOESP" + "ESQESRESSESTESUESVESWESXESYESZETFETGETHETIETJETKETLETMETNETOETPETQETRETSET" + "TETUETVETWETXETYETZEUFEUGEUHEUIEUJEUKEULEUMEUNEUOEUPEUQEUREUSEUTEUUEUVEUWE" + "UXEUYEUZEVFEVGEVHEVIEVJEVKEVLEVMEVNEVOEVPEVQEVREVSEVTEVUEVVEVWEVXEVYEVZEWF" + "EWGEWHEWIEWJEWKEWLEWMEWNEWOEWPEWQEWREWSEWTEWUEWVEWWEWXEWYEWZEXFEXGEXHEXIEX" + "JEXKEXLEXMEXNEXOEXPEXQEXREXSEXTEXUEXVEXWEXXEXYEXZEYFEYGEYHEYIEYJEYKEYLEYME" + "YNEYOEYPEYQEYREYSEYTEYUEYVEYWEYXEYYEYZEZFEZGEZHEZIEZJEZKEZLEZMEZNEZOEZPEZQ" + "EZREZSEZTEZUEZVEZWEZXEZYEZZFFFGFFHFFIFFJFFKFFLFFMFFNFFOFFPFFQFFRFFSFFTFFUF" + "FVFFWFFXFFYFFZFGGFGHFGIFGJFGKFGLFGMFGNFGOFGPFGQFGRFGSFGTFGUFGVFGWFGXFGYFGZ" + "FHGFHHFHIFHJFHKFHLFHMFHNFHOFHPFHQFHRFHSFHTFHUFHVFHWFHXFHYFHZFIGFIHFIIFIJFI" + "KFILFIMFINFIOFIPFIQFIRFISFITFIUFIVFIWFIXFIYFIZFJGFJHFJIFJJFJKFJLFJMFJNFJOF" + "JPFJQFJRFJSFJTFJUFJVFJWFJXFJYFJZFKGFKHFKIFKJFKKFKLFKMFKNFKOFKPFKQFKRFKSFKT" + "FKUFKVFKWFKXFKYFKZFLGFLHFLIFLJFLKFLLFLMFLNFLOFLPFLQFLRFLSFLTFLUFLVFLWFLXFL" + "YFLZFMGFMHFMIFMJFMKFMLFMMFMNFMOFMPFMQFMRFMSFMTFMUFMVFMWFMXFMYFMZFNGFNHFNIF" + "NJFNKFNLFNMFNNFNOFNPFNQFNRFNSFNTFNUFNVFNWFNXFNYFNZFOGFOHFOIFOJFOKFOLFOMFON" + "FOOFOPFOQFORFOSFOTFOUFOVFOWFOXFOYFOZFPGFPHFPIFPJFPKFPLFPMFPNFPOFPPFPQFPRFP" + "SFPTFPUFPVFPWFPXFPYFPZFQGFQHFQIFQJFQKFQLFQMFQNFQOFQPFQQFQRFQSFQTFQUFQVFQWF" + "QXFQYFQZFRGFRHFRIFRJFRKFRLFRMFRNFROFRPFRQFRRFRSFRTFRUFRVFRWFRXFRYFRZFSGFSH" + "FSIFSJFSKFSLFSMFSNFSOFSPFSQFSRFSSFSTFSUFSVFSWFSXFSYFSZFTGFTHFTIFTJFTKFTLFT" + "MFTNFTOFTPFTQFTRFTSFTTFTUFTVFTWFTXFTYFTZFUGFUHFUIFUJFUKFULFUMFUNFUOFUPFUQF" + "URFUSFUTFUUFUVFUWFUXFUYFUZFVGFVHFVIFVJFVKFVLFVMFVNFVOFVPFVQFVRFVSFVTFVUFVV" + "FVWFVXFVYFVZFWGFWHFWIFWJFWKFWLFWMFWNFWOFWPFWQFWRFWSFWTFWUFWVFWWFWXFWYFWZFX" + "GFXHFXIFXJFXKFXLFXMFXNFXOFXPFXQFXRFXSFXTFXUFXVFXWFXXFXYFXZFYGFYHFYIFYJFYKF" + "YLFYMFYNFYOFYPFYQFYRFYSFYTFYUFYVFYWFYXFYYFYZFZGFZHFZIFZJFZKFZLFZMFZNFZOFZP" + "FZQFZRFZSFZTFZUFZVFZWFZXFZYFZZGGGHGGIGGJGGKGGLGGMGGNGGOGGPGGQGGRGGSGGTGGUG" + "GVGGWGGXGGYGGZGHHGHIGHJGHKGHLGHMGHNGHOGHPGHQGHRGHSGHTGHUGHVGHWGHXGHYGHZGIH" + "GIIGIJGIKGILGIMGINGIOGIPGIQGIRGISGITGIUGIVGIWGIXGIYGIZGJHGJIGJJGJKGJLGJMGJ" + "NGJOGJPGJQGJRGJSGJTGJUGJVGJWGJXGJYGJZGKHGKIGKJGKKGKLGKMGKNGKOGKPGKQGKRGKSG" + "KTGKUGKVGKWGKXGKYGKZGLHGLIGLJGLKGLLGLMGLNGLOGLPGLQGLRGLSGLTGLUGLVGLWGLXGLY" + "GLZGMHGMIGMJGMKGMLGMMGMNGMOGMPGMQGMRGMSGMTGMUGMVGMWGMXGMYGMZGNHGNIGNJGNKGN" + "LGNMGNNGNOGNPGNQGNRGNSGNTGNUGNVGNWGNXGNYGNZGOHGOIGOJGOKGOLGOMGONGOOGOPGOQG" + "ORGOSGOTGOUGOVGOWGOXGOYGOZGPHGPIGPJGPKGPLGPMGPNGPOGPPGPQGPRGPSGPTGPUGPVGPW" + "GPXGPYGPZGQHGQIGQJGQKGQLGQMGQNGQOGQPGQQGQRGQSGQTGQUGQVGQWGQXGQYGQZGRHGRIGR" + "JGRKGRLGRMGRNGROGRPGRQGRRGRSGRTGRUGRVGRWGRXGRYGRZGSHGSIGSJGSKGSLGSMGSNGSOG" + "SPGSQGSRGSSGSTGSUGSVGSWGSXGSYGSZGTHGTIGTJGTKGTLGTMGTNGTOGTPGTQGTRGTSGTTGTU" + "GTVGTWGTXGTYGTZGUHGUIGUJGUKGULGUMGUNGUOGUPGUQGURGUSGUTGUUGUVGUWGUXGUYGUZGV" + "HGVIGVJGVKGVLGVMGVNGVOGVPGVQGVRGVSGVTGVUGVVGVWGVXGVYGVZGWHGWIGWJGWKGWLGWMG" + "WNGWOGWPGWQGWRGWSGWTGWUGWVGWWGWXGWYGWZGXHGXIGXJGXKGXLGXMGXNGXOGXPGXQGXRGXS" + "GXTGXUGXVGXWGXXGXYGXZGYHGYIGYJGYKGYLGYMGYNGYOGYPGYQGYRGYSGYTGYUGYVGYWGYXGY" + "YGYZGZHGZIGZJGZKGZLGZMGZNGZOGZPGZQGZRGZSGZTGZUGZVGZWGZXGZYGZZHHHIHHJHHKHHL" + "HHMHHNHHOHHPHHQHHRHHSHHTHHUHHVHHWHHXHHYHHZHIIHIJHIKHILHIMHINHIOHIPHIQHIRHI" + "SHITHIUHIVHIWHIXHIYHIZHJIHJJHJKHJLHJMHJNHJOHJPHJQHJRHJSHJTHJUHJVHJWHJXHJYH" + "JZHKIHKJHKKHKLHKMHKNHKOHKPHKQHKRHKSHKTHKUHKVHKWHKXHKYHKZHLIHLJHLKHLLHLMHLN" + "HLOHLPHLQHLRHLSHLTHLUHLVHLWHLXHLYHLZHMIHMJHMKHMLHMMHMNHMOHMPHMQHMRHMSHMTHM" + "UHMVHMWHMXHMYHMZHNIHNJHNKHNLHNMHNNHNOHNPHNQHNRHNSHNTHNUHNVHNWHNXHNYHNZHOIH" + "OJHOKHOLHOMHONHOOHOPHOQHORHOSHOTHOUHOVHOWHOXHOYHOZHPIHPJHPKHPLHPMHPNHPOHPP" + "HPQHPRHPSHPTHPUHPVHPWHPXHPYHPZHQIHQJHQKHQLHQMHQNHQOHQPHQQHQRHQSHQTHQUHQVHQ" + "WHQXHQYHQZHRIHRJHRKHRLHRMHRNHROHRPHRQHRRHRSHRTHRUHRVHRWHRXHRYHRZHSIHSJHSKH" + "SLHSMHSNHSOHSPHSQHSRHSSHSTHSUHSVHSWHSXHSYHSZHTIHTJHTKHTLHTMHTNHTOHTPHTQHTR" + "HTSHTTHTUHTVHTWHTXHTYHTZHUIHUJHUKHULHUMHUNHUOHUPHUQHURHUSHUTHUUHUVHUWHUXHU" + "YHUZHVIHVJHVKHVLHVMHVNHVOHVPHVQHVRHVSHVTHVUHVVHVWHVXHVYHVZHWIHWJHWKHWLHWMH" + "WNHWOHWPHWQHWRHWSHWTHWUHWVHWWHWXHWYHWZHXIHXJHXKHXLHXMHXNHXOHXPHXQHXRHXSHXT" + "HXUHXVHXWHXXHXYHXZHYIHYJHYKHYLHYMHYNHYOHYPHYQHYRHYSHYTHYUHYVHYWHYXHYYHYZHZ" + "IHZJHZKHZLHZMHZNHZOHZPHZQHZRHZSHZTHZUHZVHZWHZXHZYHZZIIIJIIKIILIIMIINIIOIIP" + "IIQIIRIISIITIIUIIVIIWIIXIIYIIZIJJIJKIJLIJMIJNIJOIJPIJQIJRIJSIJTIJUIJVIJWIJ" + "XIJYIJZIKJIKKIKLIKMIKNIKOIKPIKQIKRIKSIKTIKUIKVIKWIKXIKYIKZILJILKILLILMILNI" + "LOILPILQILRILSILTILUILVILWILXILYILZIMJIMKIMLIMMIMNIMOIMPIMQIMRIMSIMTIMUIMV" + "IMWIMXIMYIMZINJINKINLINMINNINOINPINQINRINSINTINUINVINWINXINYINZIOJIOKIOLIO" + "MIONIOOIOPIOQIORIOSIOTIOUIOVIOWIOXIOYIOZIPJIPKIPLIPMIPNIPOIPPIPQIPRIPSIPTI" + "PUIPVIPWIPXIPYIPZIQJIQKIQLIQMIQNIQOIQPIQQIQRIQSIQTIQUIQVIQWIQXIQYIQZIRJIRK" + "IRLIRMIRNIROIRPIRQIRRIRSIRTIRUIRVIRWIRXIRYIRZISJISKISLISMISNISOISPISQISRIS" + "SISTISUISVISWISXISYISZITJITKITLITMITNITOITPITQITRITSITTITUITVITWITXITYITZI" + "UJIUKIULIUMIUNIUOIUPIUQIURIUSIUTIUUIUVIUWIUXIUYIUZIVJIVKIVLIVMIVNIVOIVPIVQ" + "IVRIVSIVTIVUIVVIVWIVXIVYIVZIWJIWKIWLIWMIWNIWOIWPIWQIWRIWSIWTIWUIWVIWWIWXIW" + "YIWZIXJIXKIXLIXMIXNIXOIXPIXQIXRIXSIXTIXUIXVIXWIXXIXYIXZIYJIYKIYLIYMIYNIYOI" + "YPIYQIYRIYSIYTIYUIYVIYWIYXIYYIYZIZJIZKIZLIZMIZNIZOIZPIZQIZRIZSIZTIZUIZVIZW" + "IZXIZYIZZJJJKJJLJJMJJNJJOJJPJJQJJRJJSJJTJJUJJVJJWJJXJJYJJZJKKJKLJKMJKNJKOJ" + "KPJKQJKRJKSJKTJKUJKVJKWJKXJKYJKZJLKJLLJLMJLNJLOJLPJLQJLRJLSJLTJLUJLVJLWJLX" + "JLYJLZJMKJMLJMMJMNJMOJMPJMQJMRJMSJMTJMUJMVJMWJMXJMYJMZJNKJNLJNMJNNJNOJNPJN" + "QJNRJNSJNTJNUJNVJNWJNXJNYJNZJOKJOLJOMJONJOOJOPJOQJORJOSJOTJOUJOVJOWJOXJOYJ" + "OZJPKJPLJPMJPNJPOJPPJPQJPRJPSJPTJPUJPVJPWJPXJPYJPZJQKJQLJQMJQNJQOJQPJQQJQR" + "JQSJQTJQUJQVJQWJQXJQYJQZJRKJRLJRMJRNJROJRPJRQJRRJRSJRTJRUJRVJRWJRXJRYJRZJS" + "KJSLJSMJSNJSOJSPJSQJSRJSSJSTJSUJSVJSWJSXJSYJSZJTKJTLJTMJTNJTOJTPJTQJTRJTSJ" + "TTJTUJTVJTWJTXJTYJTZJUKJULJUMJUNJUOJUPJUQJURJUSJUTJUUJUVJUWJUXJUYJUZJVKJVL" + "JVMJVNJVOJVPJVQJVRJVSJVTJVUJVVJVWJVXJVYJVZJWKJWLJWMJWNJWOJWPJWQJWRJWSJWTJW" + "UJWVJWWJWXJWYJWZJXKJXLJXMJXNJXOJXPJXQJXRJXSJXTJXUJXVJXWJXXJXYJXZJYKJYLJYMJ" + "YNJYOJYPJYQJYRJYSJYTJYUJYVJYWJYXJYYJYZJZKJZLJZMJZNJZOJZPJZQJZRJZSJZTJZUJZV" + "JZWJZXJZYJZZKKKLKKMKKNKKOKKPKKQKKRKKSKKTKKUKKVKKWKKXKKYKKZKLLKLMKLNKLOKLPK" + "LQKLRKLSKLTKLUKLVKLWKLXKLYKLZKMLKMMKMNKMOKMPKMQKMRKMSKMTKMUKMVKMWKMXKMYKMZ" + "KNLKNMKNNKNOKNPKNQKNRKNSKNTKNUKNVKNWKNXKNYKNZKOLKOMKONKOOKOPKOQKORKOSKOTKO" + "UKOVKOWKOXKOYKOZKPLKPMKPNKPOKPPKPQKPRKPSKPTKPUKPVKPWKPXKPYKPZKQLKQMKQNKQOK" + "QPKQQKQRKQSKQTKQUKQVKQWKQXKQYKQZKRLKRMKRNKROKRPKRQKRRKRSKRTKRUKRVKRWKRXKRY" + "KRZKSLKSMKSNKSOKSPKSQKSRKSSKSTKSUKSVKSWKSXKSYKSZKTLKTMKTNKTOKTPKTQKTRKTSKT" + "TKTUKTVKTWKTXKTYKTZKULKUMKUNKUOKUPKUQKURKUSKUTKUUKUVKUWKUXKUYKUZKVLKVMKVNK" + "VOKVPKVQKVRKVSKVTKVUKVVKVWKVXKVYKVZKWLKWMKWNKWOKWPKWQKWRKWSKWTKWUKWVKWWKWX" + "KWYKWZKXLKXMKXNKXOKXPKXQKXRKXSKXTKXUKXVKXWKXXKXYKXZKYLKYMKYNKYOKYPKYQKYRKY" + "SKYTKYUKYVKYWKYXKYYKYZKZLKZMKZNKZOKZPKZQKZRKZSKZTKZUKZVKZWKZXKZYKZZLLLMLLN" + "LLOLLPLLQLLRLLSLLTLLULLVLLWLLXLLYLLZLMMLMNLMOLMPLMQLMRLMSLMTLMULMVLMWLMXLM" + "YLMZLNMLNNLNOLNPLNQLNRLNSLNTLNULNVLNWLNXLNYLNZLOMLONLOOLOPLOQLORLOSLOTLOUL" + "OVLOWLOXLOYLOZLPMLPNLPOLPPLPQLPRLPSLPTLPULPVLPWLPXLPYLPZLQMLQNLQOLQPLQQLQR" + "LQSLQTLQULQVLQWLQXLQYLQZLRMLRNLROLRPLRQLRRLRSLRTLRULRVLRWLRXLRYLRZLSMLSNLS" + "OLSPLSQLSRLSSLSTLSULSVLSWLSXLSYLSZLTMLTNLTOLTPLTQLTRLTSLTTLTULTVLTWLTXLTYL" + "TZLUMLUNLUOLUPLUQLURLUSLUTLUULUVLUWLUXLUYLUZLVMLVNLVOLVPLVQLVRLVSLVTLVULVV" + "LVWLVXLVYLVZLWMLWNLWOLWPLWQLWRLWSLWTLWULWVLWWLWXLWYLWZLXMLXNLXOLXPLXQLXRLX" + "SLXTLXULXVLXWLXXLXYLXZLYMLYNLYOLYPLYQLYRLYSLYTLYULYVLYWLYXLYYLYZLZMLZNLZOL" + "ZPLZQLZRLZSLZTLZULZVLZWLZXLZYLZZMMMNMMOMMPMMQMMRMMSMMTMMUMMVMMWMMXMMYMMZMN" + "NMNOMNPMNQMNRMNSMNTMNUMNVMNWMNXMNYMNZMONMOOMOPMOQMORMOSMOTMOUMOVMOWMOXMOYM" + "OZMPNMPOMPPMPQMPRMPSMPTMPUMPVMPWMPXMPYMPZMQNMQOMQPMQQMQRMQSMQTMQUMQVMQWMQX" + "MQYMQZMRNMROMRPMRQMRRMRSMRTMRUMRVMRWMRXMRYMRZMSNMSOMSPMSQMSRMSSMSTMSUMSVMS" + "WMSXMSYMSZMTNMTOMTPMTQMTRMTSMTTMTUMTVMTWMTXMTYMTZMUNMUOMUPMUQMURMUSMUTMUUM" + "UVMUWMUXMUYMUZMVNMVOMVPMVQMVRMVSMVTMVUMVVMVWMVXMVYMVZMWNMWOMWPMWQMWRMWSMWT" + "MWUMWVMWWMWXMWYMWZMXNMXOMXPMXQMXRMXSMXTMXUMXVMXWMXXMXYMXZMYNMYOMYPMYQMYRMY" + "SMYTMYUMYVMYWMYXMYYMYZMZNMZOMZPMZQMZRMZSMZTMZUMZVMZWMZXMZYMZZNNNONNPNNQNNR" + "NNSNNTNNUNNVNNWNNXNNYNNZNOONOPNOQNORNOSNOTNOUNOVNOWNOXNOYNOZNPONPPNPQNPRNP" + "SNPTNPUNPVNPWNPXNPYNPZNQONQPNQQNQRNQSNQTNQUNQVNQWNQXNQYNQZNRONRPNRQNRRNRSN" + "RTNRUNRVNRWNRXNRYNRZNSONSPNSQNSRNSSNSTNSUNSVNSWNSXNSYNSZNTONTPNTQNTRNTSNTT" + "NTUNTVNTWNTXNTYNTZNUONUPNUQNURNUSNUTNUUNUVNUWNUXNUYNUZNVONVPNVQNVRNVSNVTNV" + "UNVVNVWNVXNVYNVZNWONWPNWQNWRNWSNWTNWUNWVNWWNWXNWYNWZNXONXPNXQNXRNXSNXTNXUN" + "XVNXWNXXNXYNXZNYONYPNYQNYRNYSNYTNYUNYVNYWNYXNYYNYZNZONZPNZQNZRNZSNZTNZUNZV" + "NZWNZXNZYNZZOOOPOOQOOROOSOOTOOUOOVOOWOOXOOYOOZOPPOPQOPROPSOPTOPUOPVOPWOPXO" + "PYOPZOQPOQQOQROQSOQTOQUOQVOQWOQXOQYOQZORPORQORRORSORTORUORVORWORXORYORZOSP" + "OSQOSROSSOSTOSUOSVOSWOSXOSYOSZOTPOTQOTROTSOTTOTUOTVOTWOTXOTYOTZOUPOUQOUROU" + "SOUTOUUOUVOUWOUXOUYOUZOVPOVQOVROVSOVTOVUOVVOVWOVXOVYOVZOWPOWQOWROWSOWTOWUO" + "WVOWWOWXOWYOWZOXPOXQOXROXSOXTOXUOXVOXWOXXOXYOXZOYPOYQOYROYSOYTOYUOYVOYWOYX" + "OYYOYZOZPOZQOZROZSOZTOZUOZVOZWOZXOZYOZZPPPQPPRPPSPPTPPUPPVPPWPPXPPYPPZPQQP" + "QRPQSPQTPQUPQVPQWPQXPQYPQZPRQPRRPRSPRTPRUPRVPRWPRXPRYPRZPSQPSRPSSPSTPSUPSV" + "PSWPSXPSYPSZPTQPTRPTSPTTPTUPTVTABUABVABWABXABYABZACBACCACDACEACFACGACHACIA" + "CJACKACLACMACNACOACPACQACRACSACTACUACVACWACXACYACZADBADCADDADEADFADGADHADI" + "ADJADKADLADMADAAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAA" + "UAAVAAWAAXAAYAAZABBABCABDABEABFABGABHABIABJABKABLABMABNABOABPABQABRABSABHA" + "FIAFJAFKAFLAFMAFNAFOAFPAFQAFRAFSAFTAFUAFVAFWAFXAFYAFZAGBAGCAGDAGEAGFAGGAGH" + "AGIAGJAGKAGLAGMAGNAGOAGPAGQAGRAGSAGTAGUAGVAGWAGXAGYAGZAHNADOADPADQADRADSAD" + "TADUADVADWADXADYADZAEBAECAEDAEEAEFAEGAEHAEIAEJAEKAELAEMAENAEOAEPAEQAERAESA" + "ETAEUAEVAEWAEXAEYAEZAFBAFCAFDAFEAFFAFGAFUAIVAIWAIXAIYAIZAJBAJCAJDAJEAJFAJG" + "AJHAJIAJJAJKAJLAJMAJNAJOAJPAJQAJRAJSAJTAJUAJVAJWAJXAJYAJZAKBAKCAKDAKEAKFAK" + "GAKHAKIAKJAKKAKLAKMAKNAKBAHCAHDAHEAHFAHGAHHAHIAHJAHKAHLAHMAHNAHOAHPAHQAHRA" + "HSAHTAHUAHVAHWAHXAHYAHZAIBAICAIDAIEAIFAIGAIHAIIAIJAIKAILAIMAINAIOAIPAIQAIR" + "AISAITAIIAMJAMKAMLAMMAMNAMOAMPAMQAMRAMSAMTAMUAMVAMWAMXAMYAMZANBANCANDANEAN" + "FANGANHANIANJANKANLANMANNANOANPANQANRANSANTANUANVANWANXANYANZAOBAOOAKPAKQA" + "KRAKSAKTAKUAKVAKWAKXAKYAKZALBALCALDALEALFALGALHALIALJALKALLALMALNALOALPALQ" + "ALRALSALTALUALVALWALXALYALZAMBAMCAMDAMEAMFAMGAMHAMVAPWAPXAPYAPZAQBAQCAQDAQ" + "EAQFAQGAQHAQIAQJAQKAQLAQMAQNAQOAQPAQQAQRAQSAQTAQUAQVAQWAQXAQYAQZARBARCARDA" + "REARFARGARHARIARJARKARLARMARNAROARCAODAOEAOFAOGAOHAOIAOJAOKAOLAOMAONAOOAOP" + "AOQAORAOSAOTAOUAOVAOWAOXAOYAOZAPBAPCAPDAPEAPFAPGAPHAPIAPJAPKAPLAPMAPNAPOAP" + "PAPQAPRAPSAPTAPUAPJATKATLATMATNATOATPATQATRATSATTATUATVATWATXATYATZAUBAUCA" + "UDAUEAUFAUGAUHAUIAUJAUKAULAUMAUNAUOAUPAUQAURAUSAUTAUUAUVAUWAUXAUYAUZAVBAVC" + "AVPARQARRARSARTARUARVARWARXARYARZASBASCASDASEASFASGASHASIASJASKASLASMASNAS" + "OASPASQASRASSASTASUASVASWASXASYASZATBATCATDATEATFATGATHATIATWAWXAWYAWZAXBA" + "XCAXDAXEAXFAXGAXHAXIAXJAXKAXLAXMAXNAXOAXPAXQAXRAXSAXTAXUAXVAXWAXXAXYAXZAYB" + "AYCAYDAYEAYFAYGAYHAYIAYJAYKAYLAYMAYNAYOAYPAYDAVEAVFAVGAVHAVIAVJAVKAVLAVMAV" + "NAVOAVPAVQAVRAVSAVTAVUAVVAVWAVXAVYAVZAWBAWCAWDAWEAWFAWGAWHAWIAWJAWKAWLAWMA" + "WNAWOAWPAWQAWRAWSAWTAWUAWVAWBLBBMBBNBBOBBPBBQBBRBBSBBTBBUBBVBBWBBXBBYBBZBC" + "CBCDBCEBCFBCGBCHBCIBCJBCKBCLBCMBCNBCOBCPBCQBCRBCSBCTBCUBCVBCWBCXBCYBCZBDCB" + "DDBDEBDFBDGBQAYRAYSAYTAYUAYVAYWAYXAYYAYZAZBAZCAZDAZEAZFAZGAZHAZIAZJAZKAZLA" + "ZMAZNAZOAZPAZQAZRAZSAZTAZUAZVAZWAZXAZYAZZBBBCBBDBBEBBFBBGBBHBBIBBJBBKBFDBF" + "EBFFBFGBFHBFIBFJBFKBFLBFMBFNBFOBFPBFQBFRBFSBFTBFUBFVBFWBFXBFYBFZBGCBGDBGEB" + "GFBGGBGHBGIBGJBGKBGLBGMBGNBGOBGPBGQBGRBGSBGTBGUBGVBGWBDHBDIBDJBDKBDLBDMBDN" + "BDOBDPBDQBDRBDSBDTBDUBDVBDWBDXBDYBDZBECBEDBEEBEFBEGBEHBEIBEJBEKBELBEMBENBE" + "OBEPBEQBERBESBETBEUBEVBEWBEXBEYBEZBFCBITBIUBIVBIWBIXBIYBIZBJCBJDBJEBJFBJGB" + "JHBJIBJJBJKBJLBJMBJNBJOBJPBJQBJRBJSBJTBJUBJVBJWBJXBJYBJZBKCBKDBKEBKFBKGBKH" + "BKIBKJBKKBKLBKMBKNBKOBGXBGYBGZBHCBHDBHEBHFBHGBHHBHIBHJBHKBHLBHMBHNBHOBHPBH" + "QBHRBHSBHTBHUBHVBHWBHXBHYBHZBICBIDBIEBIFBIGBIHBIIBIJBIKBILBIMBINBIOBIPBIQB" + "IRBISBMLBMMBMNBMOBMPBMQBMRBMSBMTBMUBMVBMWBMXBMYBMZBNCBNDBNEBNFBNGBNHBNIBNJ" + "BNKBNLBNMBNNBNOBNPBNQBNRBNSBNTBNUBNVBNWBNXBNYBNZBOCBODBOEBOFBOGBKPBKQBKRBK" + "SBKTBKUBKVBKWBKXBKYBKZBLCBLDBLEBLFBLGBLHBLIBLJBLKBLLBLMBLNBLOBLPBLQBLRBLSB" + "LTBLUBLVBLWBLXBLYBLZBMCBMDBMEBMFBMGBMHBMIBMJBMKBQDBQEBQFBQGBQHBQIBQJBQKBQL" + "BQMBQNBQOBQPBQQBQRBQSBQTBQUBQVBQWBQXBQYBQZBRCBRDBREBRFBRGBRHBRIBRJBRKBRLBR" + "MBRNBROBRPBRQBRRBRSBRTBRUBRVBRWBOHBOIBOJBOKBOLBOMBONBOOBOPBOQBORBOSBOTBOUB" + "OVBOWBOXBOYBOZBPCBPDBPEBPFBPGBPHBPIBPJBPKBPLBPMBPNBPOBPPBPQBPRBPSBPTBPUBPV" + "BPWBPXBPYBPZBQCBTTBTUBTVBTWBTXBTYBTZBUCBUDBUEBUFBUGBUHBUIBUJBUKBULBUMBUNBU" + "OBUPBUQBURBUSBUTBUUBUVBUWBUXBUYBUZBVCBVDBVEBVFBVGBVHBVIBVJBVKBVLBVMBVNBVOB" + "RXBRYBRZBSCBSDBSEBSFBSGBSHBSIBSJBSKBSLBSMBSNBSOBSPBSQBSRBSSBSTBSUBSVBSWBSX" + "BSYBSZBTCBTDBTEBTFBTGBTHBTIBTJBTKBTLBTMBTNBTOBTPBTQBTRBTSBXLBXMBXNBXOBXPBX" + "QBXRBXSBXTBXUBXVBXWBXXBXYBXZBYCBYDBYEBYFBYGBYHBYIBYJBYKBYLBYMBYNBYOBYPBYQB" + "YRBYSBYTBYUBYVBYWBYXBYYBYZBZCBZDBZEBZFBZGBVPBVQBVRBVSBVTBVUBVVBVWBVXBVYBVZ" + "BWCBWDBWEBWFBWGBWHBWIBWJBWKBWLBWMBWNBWOBWPBWQBWRBWSBWTBWUBWVBWWBWXBWYBWZBX" + "CBXDBXEBXFBXGBXHBXIBXJBXKBCDFCDGCDHCDICDJCDKCDLCDMCDNCDOCDPCDQCDRCDSCDTCDU" + "CDVCDWCDXCDYCDZCEDCEECEFCEGCEHCEICEJCEKCELCEMCENCEOCEPCEQCERCESCETCEUCEVCE" + "WCEXCEYCEZZHBZIBZJBZKBZLBZMBZNBZOBZPBZQBZRBZSBZTBZUBZVBZWBZXBZYBZZCCCDCCEC" + "CFCCGCCHCCICCJCCKCCLCCMCCNCCOCCPCCQCCRCCSCCTCCUCCVCCWCCXCCYCCZCDDCDECGYCGZ" + "CHDCHECHFCHGCHHCHICHJCHKCHLCHMCHNCHOCHPCHQCHRCHSCHTCHUCHVCHWCHXCHYCHZCIDCI" + "ECIFCIGCIHCIICIJCIKCILCIMCINCIOCIPCIQCIRCISCITCIUCIVCFDCFECFFCFGCFHCFICFJC" + "FKCFLCFMCFNCFOCFPCFQCFRCFSCFTCFUCFVCFWCFXCFYCFZCGDCGECGFCGGCGHCGICGJCGKCGL" + "CGMCGNCGOCGPCGQCGRCGSCGTCGUCGVCGWCGXCKUCKVCKWCKXCKYCKZCLDCLECLFCLGCLHCLICL" + "JCLKCLLCLMCLNCLOCLPCLQCLRCLSCLTCLUCLVCLWCLXCLYCLZCMDCMECMFCMGCMHCMICMJCMKC" + "MLCMMCMNCMOCMPCMQCMRCIWCIXCIYCIZCJDCJECJFCJGCJHCJICJJCJKCJLCJMCJNCJOCJPCJQ" + "CJRCJSCJTCJUCJVCJWCJXCJYCJZCKDCKECKFCKGCKHCKICKJCKKCKLCKMCKNCKOCKPCKQCKRCK" + "SCKTCOQCORCOSCOTCOUCOVCOWCOXCOYCOZCPDCPECPFCPGCPHCPICPJCPKCPLCPMCPNCPOCPPC" + "PQCPRCPSCPTCPUCPVCPWCPXCPYCPZCQDCQECQFCQGCQHCQICQJCQKCQLCQMCQNCMSCMTCMUCMV" + "CMWCMXCMYCMZCNDCNECNFCNGCNHCNICNJCNKCNLCNMCNNCNOCNPCNQCNRCNSCNTCNUCNVCNWCN" + "XCNYCNZCODCOECOFCOGCOHCOICOJCOKCOLCOMCONCOOCOPCSMCSNCSOCSPCSQCSRCSSCSTCSUC" + "SVCSWCSXCSYCSZCTDCTECTFCTGCTHCTICTJCTKCTLCTMCTNCTOCTPCTQCTRCTSCTTCTUCTVCTW" + "CTXCTYCTZCUDCUECUFCUGCUHCUICUJCQOCQPCQQCQRCQSCQTCQUCQVCQWCQXCQYCQZCRDCRECR" + "FCRGCRHCRICRJCRKCRLCRMCRNCROCRPCRQCRRCRSCRTCRUCRVCRWCRXCRYCRZCSDCSECSFCSGC" + "SHCSICSJCSKCSLCWICWJCWKCWLCWMCWNCWOCWPCWQCWRCWSCWTCWUCWVCWWCWXCWYCWZCXDCXE" + "CXFCXGCXHCXICXJCXKCXLCXMCXNCXOCXPCXQCXRCXSCXTCXUCXVCXWCXXCXYCXZCYDCYECYFCU" + "KCULCUMCUNCUOCUPCUQCURCUSCUTCUUCUVCUWCUXCUYCUZCVDCVECVFCVGCVHCVICVJCVKCVLC" + "VMCVNCVOCVPCVQCVRCVSCVTCVUCVVCVWCVXCVYCVZCWDCWECWFCWGCWHEDDFDDGDDHDDIDDJDD" + "KDDLDDMDDNDDODDPDDQDDRDDSDDTDDUDDVDDWDDXDDYDDZDEEDEFDEGDEHDEIDEJDEKDELDEMD" + "ENDEODEPDEQDERDESDETDEUDEVDEWDEXDEYDEZDFCYGCYHCYICYJCYKCYLCYMCYNCYOCYPCYQC" + "YRCYSCYTCYUCYVCYWCYXCYYCYZCZDCZECZFCZGCZHCZICZJCZKCZLCZMCZNCZOCZPCZQCZRCZS" + "CZTCZUCZVCZWCZXCZYCZZDDDEDHFDHGDHHDHIDHJDHKDHLDHMDHNDHODHPDHQDHRDHSDHTDHUD" + "HVDHWDHXDHYDHZDIEDIFDIGDIHDIIDIJDIKDILDIMDINDIODIPDIQDIRDISDITDIUDIVDIWDIX" + "DIYDIZDJEDFFDFGDFHDFIDFJDFKDFLDFMDFNDFODFPDFQDFRDFSDFTDFUDFVDFWDFXDFYDFZDG" + "EDGFDGGDGHDGIDGJDGKDGLDGMDGNDGODGPDGQDGRDGSDGTDGUDGVDGWDGXDGYDGZDHEDLFDLGD" + "LHDLIDLJDLKDLLDLMDLNDLODLPDLQDLRDLSDLTDLUDLVDLWDLXDLYDLZDMEDMFDMGDMHDMIDMJ" + "DMKDMLDMMDMNDMODMPDMQDMRDMSDMTDMUDMVDMWDMXDMYDMZDNEDJFDJGDJHDJIDJJDJKDJLDJ" + "MDJNDJODJPDJQDJRDJSDJTDJUDJVDJWDJXDJYDJZDKEDKFDKGDKHDKIDKJDKKDKLDKMDKNDKOD" + "KPDKQDKRDKSDKTDKUDKVDKWDKXDKYDKZDLEDPFDPGDPHDPIDPJDPKDPLDPMDPNDPODPPDPQDPR" + "DPSDPTDPUDPVDPWDPXDPYDPZDQEDQFDQGDQHDQIDQJDQKDQLDQMDQNDQODQPDQQDQRDQSDQTDQ" + "UDQVDQWDQXDQYDQZDREDNFDNGDNHDNIDNJDNKDNLDNMDNNDNODNPDNQDNRDNSDNTDNUDNVDNWD" + "NXDNYDNZDOEDOFDOGDOHDOIDOJDOKDOLDOMDONDOODOPDOQDORDOSDOTDOUDOVDOWDOXDOYDOZ" + "DPEDTFDTGDTHDTIDTJDTKDTLDTMDTNDTODTPDTQDTRDTSDTTDTUDTVDTWDTXDTYDTZDUEDUFDU" + "GDUHDUIDUJDUKDULDUMDUNDUODUPDUQDURDUSDUTDUUDUVDUWDUXDUYDUZDVEDRFDRGDRHDRID" + "RJDRKDRLDRMDRNDRODRPDRQDRRDRSDRTDRUDRVDRWDRXDRYDRZDSEDSFDSGDSHDSIDSJDSKDSL" + "DSMDSNDSODSPDSQDSRDSSDSTDSUDSVDSWDSXDSYDSZDTEDXFDXGDXHDXIDXJDXKDXLDXMDXNDX" + "ODXPDXQDXRDXSDXTDXUDXVDXWDXXDXYDXZDYEDYFDYGDYHDYIDYJDYKDYLDYMDYNDYODYPDYQD" + "YRDYSDYTDYUDYVDYWDYXDYYDYZDZEDVFDVGDVHDVIDVJDVKDVLDVMDVNDVODVPDVQDVRDVSDVT" + "DVUDVVDVWDVXDVYDVZDWEDWFDWGDWHDWIDWJDWKDWLDWMDWNDWODWPDWQDWRDWSDWTDWUDWVDW" + "WDWXDWYDWZDXFGEFHEFIEFJEFKEFLEFMEFNEFOEFPEFQEFREFSEFTEFUEFVEFWEFXEFYEFZEGF" + "EGGEGHEGIEGJEGKEGLEGMEGNEGOEGPEGQEGREGSEGTEGUEGVEGWEGXEGYEGZEHFEHGEHHEEDZF" + "DZGDZHDZIDZJDZKDZLDZMDZNDZODZPDZQDZRDZSDZTDZUDZVDZWDZXDZYDZZEEEFEEGEEHEEIE" + "EJEEKEELEEMEENEEOEEPEEQEEREESEETEEUEEVEEWEEXEEYEEZEFFEJKEJLEJMEJNEJOEJPEJQ" + "EJREJSEJTEJUEJVEJWEJXEJYEJZEKFEKGEKHEKIEKJEKKEKLEKMEKNEKOEKPEKQEKREKSEKTEK" + "UEKVEKWEKXEKYEKZELFELGELHELIELJELKELLEHIEHJEHKEHLEHMEHNEHOEHPEHQEHREHSEHTE" + "HUEHVEHWEHXEHYEHZEIFEIGEIHEIIEIJEIKEILEIMEINEIOEIPEIQEIREISEITEIUEIVEIWEIX" + "EIYEIZEJFEJGEJHEJIEJJENOENPENQENRENSENTENUENVENWENXENYENZEOFEOGEOHEOIEOJEO" + "KEOLEOMEONEOOEOPEOQEOREOSEOTEOUEOVEOWEOXEOYEOZEPFEPGEPHEPIEPJEPKEPLEPMEPNE" + "POEPPELMELNELOELPELQELRELSELTELUELVELWELXELYELZEMFEMGEMHEMIEMJEMKEMLEMMEMN" + "EMOEMPEMQEMREMSEMTEMUEMVEMWEMXEMYEMZENFENGENHENIENJENKENLENMENNERSERTERUER" + "VERWERXERYERZESFESGESHESIESJESKESLESMESNESOESPESQESRESSESTESUESVESWESXESYE" + "SZETFETGETHETIETJETKETLETMETNETOETPETQETRETSETTEPQEPREPSEPTEPUEPVEPWEPXEPY" + "EPZEQFEQGEQHEQIEQJEQKEQLEQMEQNEQOEQPEQQEQREQSEQTEQUEQVEQWEQXEQYEQZERFERGER" + "HERIERJERKERLERMERNEROERPERQERREVWEVXEVYEVZEWFEWGEWHEWIEWJEWKEWLEWMEWNEWOE" + "WPEWQEWREWSEWTEWUEWVEWWEWXEWYEWZEXFEXGEXHEXIEXJEXKEXLEXMEXNEXOEXPEXQEXREXS" + "EXTEXUEXVEXWEXXETUETVETWETXETYETZEUFEUGEUHEUIEUJEUKEULEUMEUNEUOEUPEUQEUREU" + "SEUTEUUEUVEUWEUXEUYEUZEVFEVGEVHEVIEVJEVKEVLEVMEVNEVOEVPEVQEVREVSEVTEVUEVVE" + "FFGFFHFFIFFJFFKFFLFFMFFNFFOFFPFFQFFRFFSFFTFFUFFVFFWFFXFFYFFZFGGFGHFGIFGJFG" + "KFGLFGMFGNFGOFGPFGQFGRFGSFGTFGUFGVFGWFGXFGYFGZFHGFHHFHIFHJXYEXZEYFEYGEYHEY" + "IEYJEYKEYLEYMEYNEYOEYPEYQEYREYSEYTEYUEYVEYWEYXEYYEYZEZFEZGEZHEZIEZJEZKEZLE" + "ZMEZNEZOEZPEZQEZREZSEZTEZUEZVEZWEZXEZYEZZFFJOFJPFJQFJRFJSFJTFJUFJVFJWFJXFJ" + "YFJZFKGFKHFKIFKJFKKFKLFKMFKNFKOFKPFKQFKRFKSFKTFKUFKVFKWFKXFKYFKZFLGFLHFLIF" + "LJFLKFLLFLMFLNFLOFLPFLQFLRFHKFHLFHMFHNFHOFHPFHQFHRFHSFHTFHUFHVFHWFHXFHYFHZ" + "FIGFIHFIIFIJFIKFILFIMFINFIOFIPFIQFIRFISFITFIUFIVFIWFIXFIYFIZFJGFJHFJIFJJFJ" + "KFJLFJMFJNFNWFNXFNYFNZFOGFOHFOIFOJFOKFOLFOMFONFOOFOPFOQFORFOSFOTFOUFOVFOWF" + "OXFOYFOZFPGFPHFPIFPJFPKFPLFPMFPNFPOFPPFPQFPRFPSFPTFPUFPVFPWFPXFPYFPZFLSFLT" + "FLUFLVFLWFLXFLYFLZFMGFMHFMIFMJFMKFMLFMMFMNFMOFMPFMQFMRFMSFMTFMUFMVFMWFMXFM" + "YFMZFNGFNHFNIFNJFNKFNLFNMFNNFNOFNPFNQFNRFNSFNTFNUFNVFSKFSLFSMFSNFSOFSPFSQF" + "SRFSSFSTFSUFSVFSWFSXFSYFSZFTGFTHFTIFTJFTKFTLFTMFTNFTOFTPFTQFTRFTSFTTFTUFTV" + "FTWFTXFTYFTZFUGFUHFUIFUJFUKFULFUMFUNFQGFQHFQIFQJFQKFQLFQMFQNFQOFQPFQQFQRFQ" + "SFQTFQUFQVFQWFQXFQYFQZFRGFRHFRIFRJFRKFRLFRMFRNFROFRPFRQFRRFRSFRTFRUFRVFRWF" + "RXFRYFRZFSGFSHFSIFSJFWSFWTFWUFWVFWWFWXFWYFWZFXGFXHFXIFXJFXKFXLFXMFXNFXOFXP" + "FXQFXRFXSFXTFXUFXVFXWFXXFXYFXZFYGFYHFYIFYJFYKFYLFYMFYNFYOFYPFYQFYRFYSFYTFY" + "UFYVFUOFUPFUQFURFUSFUTFUUFUVFUWFUXFUYFUZFVGFVHFVIFVJFVKFVLFVMFVNFVOFVPFVQF" + "VRFVSFVTFVUFVVFVWFVXFVYFVZFWGFWHFWIFWJFWKFWLFWMFWNFWOFWPFWQFWRHGHIGHJGHKGH" + "LGHMGHNGHOGHPGHQGHRGHSGHTGHUGHVGHWGHXGHYGHZGIHGIIGIJGIKGILGIMGINGIOGIPGIQG" + "IRGISGITGIUGIVGIWGIXGIYGIZGJHGJIGJJGJKGJLGJMGJFYWFYXFYYFYZFZGFZHFZIFZJFZKF" + "ZLFZMFZNFZOFZPFZQFZRFZSFZTFZUFZVFZWFZXFZYFZZGGGHGGIGGJGGKGGLGGMGGNGGOGGPGG" + "QGGRGGSGGTGGUGGVGGWGGXGGYGGZGHTGLUGLVGLWGLXGLYGLZGMHGMIGMJGMKGMLGMMGMNGMOG" + "MPGMQGMRGMSGMTGMUGMVGMWGMXGMYGMZGNHGNIGNJGNKGNLGNMGNNGNOGNPGNQGNRGNSGNTGNU" + "GNVGNWGNXGNYGNNGJOGJPGJQGJRGJSGJTGJUGJVGJWGJXGJYGJZGKHGKIGKJGKKGKLGKMGKNGK" + "OGKPGKQGKRGKSGKTGKUGKVGKWGKXGKYGKZGLHGLIGLJGLKGLLGLMGLNGLOGLPGLQGLRGLSGLMG" + "QNGQOGQPGQQGQRGQSGQTGQUGQVGQWGQXGQYGQZGRHGRIGRJGRKGRLGRMGRNGROGRPGRQGRRGRS" + "GRTGRUGRVGRWGRXGRYGRZGSHGSIGSJGSKGSLGSMGSNGSOGSPGSQGSRGSZGOHGOIGOJGOKGOLGO" + "MGONGOOGOPGOQGORGOSGOTGOUGOVGOWGOXGOYGOZGPHGPIGPJGPKGPLGPMGPNGPOGPPGPQGPRG" + "PSGPTGPUGPVGPWGPXGPYGPZGQHGQIGQJGQKGQLGQYGUZGVHGVIGVJGVKGVLGVMGVNGVOGVPGVQ" + "GVRGVSGVTGVUGVVGVWGVXGVYGVZGWHGWIGWJGWKGWLGWMGWNGWOGWPGWQGWRGWSGWTGWUGWVGW" + "WGWXGWYGWZGXHGXIGXJGXKGXSGSTGSUGSVGSWGSXGSYGSZGTHGTIGTJGTKGTLGTMGTNGTOGTPG" + "TQGTRGTSGTTGTUGTVGTWGTXGTYGTZGUHGUIGUJGUKGULGUMGUNGUOGUPGUQGURGUSGUTGUUGUV" + "GUWGUXGURGZSGZTGZUGZVGZWGZXGZYGZZHHHIHHJHHKHHLHHMHHNHHOHHPHHQHHRHHSHHTHHUH" + "HVHHWHHXHHYHHZHIIHIJHIKHILHIMHINHIOHIPHIQHIRHISHITHIUHIVHIWHIXHIYHLGXMGXNG" + "XOGXPGXQGXRGXSGXTGXUGXVGXWGXXGXYGXZGYHGYIGYJGYKGYLGYMGYNGYOGYPGYQGYRGYSGYT" + "GYUGYVGYWGYXGYYGYZGZHGZIGZJGZKGZLGZMGZNGZOGZPGZQGZLPHLQHLRHLSHLTHLUHLVHLWH" + "LXHLYHLZHMIHMJHMKHMLHMMHMNHMOHMPHMQHMRHMSHMTHMUHMVHMWHMXHMYHMZHNIHNJHNKHNL" + "HNMHNNHNOHNPHNQHNRHNSHNTHNUHNVHNWHIZHJIHJJHJKHJLHJMHJNHJOHJPHJQHJRHJSHJTHJ" + "UHJVHJWHJXHJYHJZHKIHKJHKKHKLHKMHKNHKOHKPHKQHKRHKSHKTHKUHKVHKWHKXHKYHKZHLIH" + "LJHLKHLLHLMHLNHLOHQNHQOHQPHQQHQRHQSHQTHQUHQVHQWHQXHQYHQZHRIHRJHRKHRLHRMHRN" + "HROHRPHRQHRRHRSHRTHRUHRVHRWHRXHRYHRZHSIHSJHSKHSLHSMHSNHSOHSPHSQHSRHSSHSTHS" + "UHNXHNYHNZHOIHOJHOKHOLHOMHONHOOHOPHOQHORHOSHOTHOUHOVHOWHOXHOYHOZHPIHPJHPKH" + "PLHPMHPNHPOHPPHPQHPRHPSHPTHPUHPVHPWHPXHPYHPZHQIHQJHQKHQLHQMHVLHVMHVNHVOHVP" + "HVQHVRHVSHVTHVUHVVHVWHVXHVYHVZHWIHWJHWKHWLHWMHWNHWOHWPHWQHWRHWSHWTHWUHWVHW" + "WHWXHWYHWZHXIHXJHXKHXLHXMHXNHXOHXPHXQHXRHXSHSVHSWHSXHSYHSZHTIHTJHTKHTLHTMH" + "TNHTOHTPHTQHTRHTSHTTHTUHTVHTWHTXHTYHTZHUIHUJHUKHULHUMHUNHUOHUPHUQHURHUSHUT" + "HUUHUVHUWHUXHUYHUZHVIHVJHVKHIIKIILIIMIINIIOIIPIIQIIRIISIITIIUIIVIIWIIXIIYI" + "IZIJJIJKIJLIJMIJNIJOIJPIJQIJRIJSIJTIJUIJVIJWIJXIJYIJZIKJIKKIKLIKMIKNIKOIKP" + "IKQIKRIKSIKTXTHXUHXVHXWHXXHXYHXZHYIHYJHYKHYLHYMHYNHYOHYPHYQHYRHYSHYTHYUHYV" + "HYWHYXHYYHYZHZIHZJHZKHZLHZMHZNHZOHZPHZQHZRHZSHZTHZUHZVHZWHZXHZYHZZIIIJINNI" + "NOINPINQINRINSINTINUINVINWINXINYINZIOJIOKIOLIOMIONIOOIOPIOQIORIOSIOTIOUIOV" + "IOWIOXIOYIOZIPJIPKIPLIPMIPNIPOIPPIPQIPRIPSIPTIPUIPVIPWIKUIKVIKWIKXIKYIKZIL" + "JILKILLILMILNILOILPILQILRILSILTILUILVILWILXILYILZIMJIMKIMLIMMIMNIMOIMPIMQI" + "MRIMSIMTIMUIMVIMWIMXIMYIMZINJINKINLINMISQISRISSISTISUISVISWISXISYISZITJITK" + "ITLITMITNITOITPITQITRITSITTITUITVITWITXITYITZIUJIUKIULIUMIUNIUOIUPIUQIURIU" + "SIUTIUUIUVIUWIUXIUYIUZIPXIPYIPZIQJIQKIQLIQMIQNIQOIQPIQQIQRIQSIQTIQUIQVIQWI" + "QXIQYIQZIRJIRKIRLIRMIRNIROIRPIRQIRRIRSIRTIRUIRVIRWIRXIRYIRZISJISKISLISMISN" + "ISOISPIXTIXUIXVIXWIXXIXYIXZIYJIYKIYLIYMIYNIYOIYPIYQIYRIYSIYTIYUIYVIYWIYXIY" + "YIYZIZJIZKIZLIZMIZNIZOIZPIZQIZRIZSIZTIZUIZVIZWIZXIZYIZZJJJKJJLJJIVJIVKIVLI" + "VMIVNIVOIVPIVQIVRIVSIVTIVUIVVIVWIVXIVYIVZIWJIWKIWLIWMIWNIWOIWPIWQIWRIWSIWT" + "IWUIWVIWWIWXIWYIWZIXJIXKIXLIXMIXNIXOIXPIXQIXRIXSYJLZJMKJMLJMMJMNJMOJMPJMQJ" + "MRJMSJMTJMUJMVJMWJMXJMYJMZJNKJNLJNMJNNJNOJNPJNQJNRJNSJNTJNUJNVJNWJNXJNYJNZ" + "JOKJOLJOMJONJOOJOPJOQJORJOSJOTJOMJJNJJOJJPJJQJJRJJSJJTJJUJJVJJWJJXJJYJJZJK" + "KJKLJKMJKNJKOJKPJKQJKRJKSJKTJKUJKVJKWJKXJKYJKZJLKJLLJLMJLNJLOJLPJLQJLRJLSJ" + "LTJLUJLVJLWJLXJLQJRRJRSJRTJRUJRVJRWJRXJRYJRZJSKJSLJSMJSNJSOJSPJSQJSRJSSJST" + "JSUJSVJSWJSXJSYJSZJTKJTLJTMJTNJTOJTPJTQJTRJTSJTTJTUJTVJTWJTXJTYJTZJUKJULJU" + "UJOVJOWJOXJOYJOZJPKJPLJPMJPNJPOJPPJPQJPRJPSJPTJPUJPVJPWJPXJPYJPZJQKJQLJQMJ" + "QNJQOJQPJQQJQRJQSJQTJQUJQVJQWJQXJQYJQZJRKJRLJRMJRNJROJRPJRYJWZJXKJXLJXMJXN" + "JXOJXPJXQJXRJXSJXTJXUJXVJXWJXXJXYJXZJYKJYLJYMJYNJYOJYPJYQJYRJYSJYTJYUJYVJY" + "WJYXJYYJYZJZKJZLJZMJZNJZOJZPJZQJZRJZSJZTJZMJUNJUOJUPJUQJURJUSJUTJUUJUVJUWJ" + "UXJUYJUZJVKJVLJVMJVNJVOJVPJVQJVRJVSJVTJVUJVVJVWJVXJVYJVZJWKJWLJWMJWNJWOJWP" + "JWQJWRJWSJWTJWUJWVJWWJWXJWMTKMUKMVKMWKMXKMYKMZKNLKNMKNNKNOKNPKNQKNRKNSKNTK" + "NUKNVKNWKNXKNYKNZKOLKOMKONKOOKOPKOQKORKOSKOTKOUKOVKOWKOXKOYKOZKPLKPMKPNKPO" + "KPPKPQKPRKUJZVJZWJZXJZYJZZKKKLKKMKKNKKOKKPKKQKKRKKSKKTKKUKKVKKWKKXKKYKKZKL" + "LKLMKLNKLOKLPKLQKLRKLSKLTKLUKLVKLWKLXKLYKLZKMLKMMKMNKMOKMPKMQKMRKMSKSRKSSK" + "STKSUKSVKSWKSXKSYKSZKTLKTMKTNKTOKTPKTQKTRKTSKTTKTUKTVKTWKTXKTYKTZKULKUMKUN" + "KUOKUPKUQKURKUSKUTKUUKUVKUWKUXKUYKUZKVLKVMKVNKVOKVPKPSKPTKPUKPVKPWKPXKPYKP" + "ZKQLKQMKQNKQOKQPKQQKQRKQSKQTKQUKQVKQWKQXKQYKQZKRLKRMKRNKROKRPKRQKRRKRSKRTK" + "RUKRVKRWKRXKRYKRZKSLKSMKSNKSOKSPKSQKYPKYQKYRKYSKYTKYUKYVKYWKYXKYYKYZKZLKZM" + "KZNKZOKZPKZQKZRKZSKZTKZUKZVKZWKZXKZYKZZLLLMLLNLLOLLPLLQLLRLLSLLTLLULLVLLWL" + "LXLLYLLZLMMLMNLMOLMPVQKVRKVSKVTKVUKVVKVWKVXKVYKVZKWLKWMKWNKWOKWPKWQKWRKWSK" + "WTKWUKWVKWWKWXKWYKWZKXLKXMKXNKXOKXPKXQKXRKXSKXTKXUKXVKXWKXXKXYKXZKYLKYMKYN" + "KYOKLPSLPTLPULPVLPWLPXLPYLPZLQMLQNLQOLQPLQQLQRLQSLQTLQULQVLQWLQXLQYLQZLRML" + "RNLROLRPLRQLRRLRSLRTLRULRVLRWLRXLRYLRZLSMLSNLSOLSPLSQLSRLSSLSTLMQLMRLMSLMT" + "LMULMVLMWLMXLMYLMZLNMLNNLNOLNPLNQLNRLNSLNTLNULNVLNWLNXLNYLNZLOMLONLOOLOPLO" + "QLORLOSLOTLOULOVLOWLOXLOYLOZLPMLPNLPOLPPLPQLPRLVWLVXLVYLVZLWMLWNLWOLWPLWQL" + "WRLWSLWTLWULWVLWWLWXLWYLWZLXMLXNLXOLXPLXQLXRLXSLXTLXULXVLXWLXXLXYLXZLYMLYN" + "LYOLYPLYQLYRLYSLYTLYULYVLYWLYXLSULSVLSWLSXLSYLSZLTMLTNLTOLTPLTQLTRLTSLTTLT" + "ULTVLTWLTXLTYLTZLUMLUNLUOLUPLUQLURLUSLUTLUULUVLUWLUXLUYLUZLVMLVNLVOLVPLVQL" + "VRLVSLVTLVULVVOMOPMOQMORMOSMOTMOUMOVMOWMOXMOYMOZMPNMPOMPPMPQMPRMPSMPTMPUMP" + "VMPWMPXMPYMPZMQNMQOMQPMQQMQRMQSMQTMQUMQVMQWMQXMQYMQZMRNMROMRPMRQMRRMRSMRLY" + "YLYZLZMLZNLZOLZPLZQLZRLZSLZTLZULZVLZWLZXLZYLZZMMMNMMOMMPMMQMMRMMSMMTMMUMMV" + "MMWMMXMMYMMZMNNMNOMNPMNQMNRMNSMNTMNUMNVMNWMNXMNYMNZMONMOYMUZMVNMVOMVPMVQMV" + "RMVSMVTMVUMVVMVWMVXMVYMVZMWNMWOMWPMWQMWRMWSMWTMWUMWVMWWMWXMWYMWZMXNMXOMXPM" + "XQMXRMXSMXTMXUMXVMXWMXXMXYMXZMYNMYOMYPMYTMRUMRVMRWMRXMRYMRZMSNMSOMSPMSQMSR" + "MSSMSTMSUMSVMSWMSXMSYMSZMTNMTOMTPMTQMTRMTSMTTMTUMTVMTWMTXMTYMTZMUNMUOMUPMU" + "QMURMUSMUTMUUMUVMUWMUXMUOXNOYNOZNPONPPNPQNPRNPSNPTNPUNPVNPWNPXNPYNPZNQONQP" + "NQQNQRNQSNQTNQUNQVNQWNQXNQYNQZNRONRPNRQNRRNRSNRTNRUNRVNRWNRXNRYNRZNSONSPNS" + "QNSRNSSNQMYRMYSMYTMYUMYVMYWMYXMYYMYZMZNMZOMZPMZQMZRMZSMZTMZUMZVMZWMZXMZYMZ" + "ZNNNONNPNNQNNRNNSNNTNNUNNVNNWNNXNNYNNZNOONOPNOQNORNOSNOTNOUNOVNOWNWPNWQNWR" + "NWSNWTNWUNWVNWWNWXNWYNWZNXONXPNXQNXRNXSNXTNXUNXVNXWNXXNXYNXZNYONYPNYQNYRNY" + "SNYTNYUNYVNYWNYXNYYNYZNZONZPNZQNZRNZSNZTNZUNZVNZWNSTNSUNSVNSWNSXNSYNSZNTON" + "TPNTQNTRNTSNTTNTUNTVNTWNTXNTYNTZNUONUPNUQNURNUSNUTNUUNUVNUWNUXNUYNUZNVONVP" + "NVQNVRNVSNVTNVUNVVNVWNVXNVYNVZNWONORXORYORZOSPOSQOSROSSOSTOSUOSVOSWOSXOSYO" + "SZOTPOTQOTROTSOTTOTUOTVOTWOTXOTYOTZOUPOUQOUROUSOUTOUUOUVOUWOUXOUYOUZOVPOVQ" + "OVROVSOVTOVUOVVOVWZXNZYNZZOOOPOOQOOROOSOOTOOUOOVOOWOOXOOYOOZOPPOPQOPROPSOP" + "TOPUOPVOPWOPXOPYOPZOQPOQQOQROQSOQTOQUOQVOQWOQXOQYOQZORPORQORRORSORTORUORVO" + "RWOZXOZYOZZPPPQPPRPPSPPTPPUPPVPPWPPXPPYPPZPQQPQRPQSPQTPQUPQVPQWPQXPQYPQZPR" + "QPRRPRSPRTPRUPRVPRWPRXPRYPRZPSQPSRPSSPSTPSUPSVPSWPSXPSYPSZPTOVXOVYOVZOWPOW" + "QOWROWSOWTOWUOWVOWWOWXOWYOWZOXPOXQOXROXSOXTOXUOXVOXWOXXOXYOXZOYPOYQOYROYSO" + "YTOYUOYVOYWOYXOYYOYZOZPOZQOZROZSOZTOZUOZVOZWQPTRPTSPTTPTUPTV"; + +TEST(ZlibTest, DeflateZFixedCorruption) { + // Tavis's test case from + // https://www.openwall.com/lists/oss-security/2022/03/26/1 + // The upstream fix, + // https://github.com/madler/zlib/commit/5c44459c3b28a9bd3283aaceab7c615f8020c531, + // was merged in Chromium in #583975. + + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + + int level = Z_DEFAULT_COMPRESSION; + int windowBits = 15; + int memLevel = 1; + int strategy = Z_FIXED; + + int ret = + deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + ASSERT_EQ(ret, Z_OK); + std::vector compressed( + deflateBound(&stream, strlen(zFixedCorruptionData))); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + stream.next_in = (uint8_t*)zFixedCorruptionData; + stream.avail_in = strlen(zFixedCorruptionData); + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + size_t compressed_sz = compressed.size() - stream.avail_out; + deflateEnd(&stream); + + std::vector decompressed(strlen(zFixedCorruptionData)); + ret = inflateInit2(&stream, windowBits); + ASSERT_EQ(ret, Z_OK); + stream.next_in = compressed.data(); + stream.avail_in = compressed_sz; + stream.next_out = decompressed.data(); + stream.avail_out = decompressed.size(); + ret = inflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + inflateEnd(&stream); + + EXPECT_EQ(decompressed.size(), strlen(zFixedCorruptionData)); + EXPECT_EQ( + memcmp(zFixedCorruptionData, decompressed.data(), decompressed.size()), + 0); +} diff --git a/deps/zlib/cpu_features.c b/deps/zlib/cpu_features.c new file mode 100644 index 00000000000000..70f01beebe30b5 --- /dev/null +++ b/deps/zlib/cpu_features.c @@ -0,0 +1,167 @@ +/* cpu_features.c -- Processor features detection. + * + * Copyright 2018 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the Chromium source repository LICENSE file. + */ + +#include "cpu_features.h" +#include "zutil.h" + +#include +#if defined(_MSC_VER) +#include +#elif defined(ADLER32_SIMD_SSSE3) +#include +#endif + +/* TODO(cavalcantii): remove checks for x86_flags on deflate. + */ +#if defined(ARMV8_OS_MACOS) +/* crc32 is a baseline feature in ARMv8.1-A, and macOS running on arm64 is new + * enough that this can be assumed without runtime detection. */ +int ZLIB_INTERNAL arm_cpu_enable_crc32 = 1; +#else +int ZLIB_INTERNAL arm_cpu_enable_crc32 = 0; +#endif +int ZLIB_INTERNAL arm_cpu_enable_pmull = 0; +int ZLIB_INTERNAL x86_cpu_enable_sse2 = 0; +int ZLIB_INTERNAL x86_cpu_enable_ssse3 = 0; +int ZLIB_INTERNAL x86_cpu_enable_simd = 0; + +#ifndef CPU_NO_SIMD + +#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_FUCHSIA) +#include +#endif + +#if defined(ARMV8_OS_ANDROID) +#include +#elif defined(ARMV8_OS_LINUX) +#include +#include +#elif defined(ARMV8_OS_FUCHSIA) +#include +#include +#include +#elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS) +#include +#elif !defined(_MSC_VER) +#include +#else +#error cpu_features.c CPU feature detection in not defined for your platform +#endif + +#if !defined(CPU_NO_SIMD) && !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS) +static void _cpu_check_features(void); +#endif + +#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_MACOS) || defined(ARMV8_OS_FUCHSIA) || defined(X86_NOT_WINDOWS) +#if !defined(ARMV8_OS_MACOS) +// _cpu_check_features() doesn't need to do anything on mac/arm since all +// features are known at build time, so don't call it. +// Do provide cpu_check_features() (with a no-op implementation) so that we +// don't have to make all callers of it check for mac/arm. +static pthread_once_t cpu_check_inited_once = PTHREAD_ONCE_INIT; +#endif +void ZLIB_INTERNAL cpu_check_features(void) +{ +#if !defined(ARMV8_OS_MACOS) + pthread_once(&cpu_check_inited_once, _cpu_check_features); +#endif +} +#elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS) +static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT; +static BOOL CALLBACK _cpu_check_features_forwarder(PINIT_ONCE once, PVOID param, PVOID* context) +{ + _cpu_check_features(); + return TRUE; +} +void ZLIB_INTERNAL cpu_check_features(void) +{ + InitOnceExecuteOnce(&cpu_check_inited_once, _cpu_check_features_forwarder, + NULL, NULL); +} +#endif + +#if (defined(__ARM_NEON__) || defined(__ARM_NEON)) +/* + * iOS@ARM is a special case where we always have NEON but don't check + * for crypto extensions. + */ +#if !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS) +/* + * See http://bit.ly/2CcoEsr for run-time detection of ARM features and also + * crbug.com/931275 for android_getCpuFeatures() use in the Android sandbox. + */ +static void _cpu_check_features(void) +{ +#if defined(ARMV8_OS_ANDROID) && defined(__aarch64__) + uint64_t features = android_getCpuFeatures(); + arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM64_FEATURE_CRC32); + arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM64_FEATURE_PMULL); +#elif defined(ARMV8_OS_ANDROID) /* aarch32 */ + uint64_t features = android_getCpuFeatures(); + arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM_FEATURE_CRC32); + arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM_FEATURE_PMULL); +#elif defined(ARMV8_OS_LINUX) && defined(__aarch64__) + unsigned long features = getauxval(AT_HWCAP); + arm_cpu_enable_crc32 = !!(features & HWCAP_CRC32); + arm_cpu_enable_pmull = !!(features & HWCAP_PMULL); +#elif defined(ARMV8_OS_LINUX) && (defined(__ARM_NEON) || defined(__ARM_NEON__)) + /* Query HWCAP2 for ARMV8-A SoCs running in aarch32 mode */ + unsigned long features = getauxval(AT_HWCAP2); + arm_cpu_enable_crc32 = !!(features & HWCAP2_CRC32); + arm_cpu_enable_pmull = !!(features & HWCAP2_PMULL); +#elif defined(ARMV8_OS_FUCHSIA) + uint32_t features; + zx_status_t rc = zx_system_get_features(ZX_FEATURE_KIND_CPU, &features); + if (rc != ZX_OK || (features & ZX_ARM64_FEATURE_ISA_ASIMD) == 0) + return; /* Report nothing if ASIMD(NEON) is missing */ + arm_cpu_enable_crc32 = !!(features & ZX_ARM64_FEATURE_ISA_CRC32); + arm_cpu_enable_pmull = !!(features & ZX_ARM64_FEATURE_ISA_PMULL); +#elif defined(ARMV8_OS_WINDOWS) + arm_cpu_enable_crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE); + arm_cpu_enable_pmull = IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); +#endif +} +#endif +#elif defined(X86_NOT_WINDOWS) || defined(X86_WINDOWS) +/* + * iOS@x86 (i.e. emulator) is another special case where we disable + * SIMD optimizations. + */ +#ifndef CPU_NO_SIMD +/* On x86 we simply use a instruction to check the CPU features. + * (i.e. CPUID). + */ +static void _cpu_check_features(void) +{ + int x86_cpu_has_sse2; + int x86_cpu_has_ssse3; + int x86_cpu_has_sse42; + int x86_cpu_has_pclmulqdq; + int abcd[4]; + +#ifdef _MSC_VER + __cpuid(abcd, 1); +#else + __cpuid(1, abcd[0], abcd[1], abcd[2], abcd[3]); +#endif + + x86_cpu_has_sse2 = abcd[3] & 0x4000000; + x86_cpu_has_ssse3 = abcd[2] & 0x000200; + x86_cpu_has_sse42 = abcd[2] & 0x100000; + x86_cpu_has_pclmulqdq = abcd[2] & 0x2; + + x86_cpu_enable_sse2 = x86_cpu_has_sse2; + + x86_cpu_enable_ssse3 = x86_cpu_has_ssse3; + + x86_cpu_enable_simd = x86_cpu_has_sse2 && + x86_cpu_has_sse42 && + x86_cpu_has_pclmulqdq; +} +#endif +#endif +#endif diff --git a/deps/zlib/arm_features.h b/deps/zlib/cpu_features.h similarity index 53% rename from deps/zlib/arm_features.h rename to deps/zlib/cpu_features.h index 09fec259b1c924..c7b15c5597623f 100644 --- a/deps/zlib/arm_features.h +++ b/deps/zlib/cpu_features.h @@ -1,4 +1,4 @@ -/* arm_features.h -- ARM processor features detection. +/* cpu_features.h -- Processor features detection. * * Copyright 2018 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be @@ -7,7 +7,12 @@ #include "zlib.h" +/* TODO(cavalcantii): remove checks for x86_flags on deflate. + */ extern int arm_cpu_enable_crc32; extern int arm_cpu_enable_pmull; +extern int x86_cpu_enable_sse2; +extern int x86_cpu_enable_ssse3; +extern int x86_cpu_enable_simd; -void arm_check_features(void); +void cpu_check_features(void); diff --git a/deps/zlib/crc32.c b/deps/zlib/crc32.c index e95b9087351c1a..d4c3248d98415b 100644 --- a/deps/zlib/crc32.c +++ b/deps/zlib/crc32.c @@ -29,13 +29,10 @@ #endif /* MAKECRCH */ #include "deflate.h" -#include "x86.h" +#include "cpu_features.h" #include "zutil.h" /* for STDC and FAR definitions */ -#if defined(CRC32_SIMD_SSE42_PCLMUL) -#include "crc32_simd.h" -#elif defined(CRC32_ARMV8_CRC32) -#include "arm_features.h" +#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32) #include "crc32_simd.h" #endif @@ -226,7 +223,7 @@ unsigned long ZEXPORT crc32_z(crc, buf, len) */ if (buf == Z_NULL) { if (!len) /* Assume user is calling crc32(0, NULL, 0); */ - x86_check_features(); + cpu_check_features(); return 0UL; } @@ -289,7 +286,7 @@ unsigned long ZEXPORT crc32(crc, buf, len) */ if (buf == Z_NULL) { if (!len) /* Assume user is calling crc32(0, NULL, 0); */ - arm_check_features(); + cpu_check_features(); return 0UL; } @@ -500,25 +497,31 @@ uLong ZEXPORT crc32_combine64(crc1, crc2, len2) ZLIB_INTERNAL void crc_reset(deflate_state *const s) { +#ifdef CRC32_SIMD_SSE42_PCLMUL if (x86_cpu_enable_simd) { crc_fold_init(s); return; } +#endif s->strm->adler = crc32(0L, Z_NULL, 0); } ZLIB_INTERNAL void crc_finalize(deflate_state *const s) { +#ifdef CRC32_SIMD_SSE42_PCLMUL if (x86_cpu_enable_simd) s->strm->adler = crc_fold_512to32(s); +#endif } ZLIB_INTERNAL void copy_with_crc(z_streamp strm, Bytef *dst, long size) { +#ifdef CRC32_SIMD_SSE42_PCLMUL if (x86_cpu_enable_simd) { crc_fold_copy(strm->state, dst, strm->next_in, size); return; } +#endif zmemcpy(dst, strm->next_in, size); strm->adler = crc32(strm->adler, dst, size); } diff --git a/deps/zlib/crc32_simd.c b/deps/zlib/crc32_simd.c index 27481847e97b90..c8e5592f38ef1e 100644 --- a/deps/zlib/crc32_simd.c +++ b/deps/zlib/crc32_simd.c @@ -8,9 +8,6 @@ #include "crc32_simd.h" #if defined(CRC32_SIMD_SSE42_PCLMUL) -#ifndef __GNUC__ -#define __attribute__() -#endif /* * crc32_sse42_simd_(): compute the crc32 of the buffer, where the buffer @@ -24,7 +21,6 @@ #include #include -__attribute__((target("sse4.2,pclmul"))) uint32_t ZLIB_INTERNAL crc32_sse42_simd_( /* SSE4.2+PCLMUL */ const unsigned char *buf, z_size_t len, diff --git a/deps/zlib/crc_folding.c b/deps/zlib/crc_folding.c index 54f4b5c9401089..ee31d4918d6413 100644 --- a/deps/zlib/crc_folding.c +++ b/deps/zlib/crc_folding.c @@ -18,15 +18,13 @@ #include "deflate.h" +#ifdef CRC32_SIMD_SSE42_PCLMUL + #include #include #include #include -#ifndef __GNUC__ -#define __attribute__() -#endif - #define CRC_LOAD(s) \ do { \ __m128i xmm_crc0 = _mm_loadu_si128((__m128i *)s->crc0 + 0);\ @@ -43,7 +41,6 @@ _mm_storeu_si128((__m128i *)s->crc0 + 4, xmm_crc_part);\ } while (0); -__attribute__((target("sse4.2,pclmul"))) ZLIB_INTERNAL void crc_fold_init(deflate_state *const s) { CRC_LOAD(s) @@ -58,7 +55,6 @@ ZLIB_INTERNAL void crc_fold_init(deflate_state *const s) s->strm->adler = 0; } -__attribute__((target("sse4.2,pclmul"))) local void fold_1(deflate_state *const s, __m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) @@ -85,7 +81,6 @@ local void fold_1(deflate_state *const s, *xmm_crc3 = _mm_castps_si128(ps_res); } -__attribute__((target("sse4.2,pclmul"))) local void fold_2(deflate_state *const s, __m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) @@ -120,7 +115,6 @@ local void fold_2(deflate_state *const s, *xmm_crc3 = _mm_castps_si128(ps_res31); } -__attribute__((target("sse4.2,pclmul"))) local void fold_3(deflate_state *const s, __m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) @@ -161,7 +155,6 @@ local void fold_3(deflate_state *const s, *xmm_crc3 = _mm_castps_si128(ps_res32); } -__attribute__((target("sse4.2,pclmul"))) local void fold_4(deflate_state *const s, __m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3) @@ -228,7 +221,6 @@ local const unsigned zalign(32) pshufb_shf_table[60] = { 0x0201008f,0x06050403,0x0a090807,0x0e0d0c0b /* shl 1 (16 -15)/shr15*/ }; -__attribute__((target("sse4.2,pclmul"))) local void partial_fold(deflate_state *const s, const size_t len, __m128i *xmm_crc0, __m128i *xmm_crc1, __m128i *xmm_crc2, __m128i *xmm_crc3, @@ -279,7 +271,6 @@ local void partial_fold(deflate_state *const s, const size_t len, *xmm_crc3 = _mm_castps_si128(ps_res); } -__attribute__((target("sse4.2,pclmul"))) ZLIB_INTERNAL void crc_fold_copy(deflate_state *const s, unsigned char *dst, const unsigned char *src, long len) { @@ -294,7 +285,7 @@ ZLIB_INTERNAL void crc_fold_copy(deflate_state *const s, goto partial; } - algn_diff = 0 - (uintptr_t)src & 0xF; + algn_diff = (0 - (uintptr_t)src) & 0xF; if (algn_diff) { xmm_crc_part = _mm_loadu_si128((__m128i *)src); _mm_storeu_si128((__m128i *)dst, xmm_crc_part); @@ -436,7 +427,6 @@ local const unsigned zalign(16) crc_mask2[4] = { 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; -__attribute__((target("sse4.2,pclmul"))) unsigned ZLIB_INTERNAL crc_fold_512to32(deflate_state *const s) { const __m128i xmm_mask = _mm_load_si128((__m128i *)crc_mask); @@ -503,3 +493,5 @@ unsigned ZLIB_INTERNAL crc_fold_512to32(deflate_state *const s) return ~crc; CRC_SAVE(s) } + +#endif /* CRC32_SIMD_SSE42_PCLMUL */ diff --git a/deps/zlib/deflate.c b/deps/zlib/deflate.c index 201254ac1b664e..87d8e3b432a95c 100644 --- a/deps/zlib/deflate.c +++ b/deps/zlib/deflate.c @@ -50,7 +50,7 @@ /* @(#) $Id$ */ #include #include "deflate.h" -#include "x86.h" +#include "cpu_features.h" #include "contrib/optimizations/insert_string.h" #if (defined(__ARM_NEON__) || defined(__ARM_NEON)) @@ -60,6 +60,11 @@ #include "crc32_simd.h" #endif +#ifdef FASTEST +/* See http://crbug.com/1113596 */ +#error "FASTEST is not supported in Chromium's zlib." +#endif + const char deflate_copyright[] = " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler "; /* @@ -171,10 +176,15 @@ local const config configuration_table[10] = { /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. + * TODO(cavalcantii): optimization opportunity, check comments on: + * https://chromium-review.googlesource.com/c/chromium/src/+/3561506/ */ #define CLEAR_HASH(s) \ - s->head[s->hash_size-1] = NIL; \ - zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + do { \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, \ + (unsigned)(s->hash_size-1)*sizeof(*s->head)); \ + } while (0) /* =========================================================================== * Slide the hash table when sliding the window down (could be avoided with 32 @@ -244,10 +254,8 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, // for all wrapper formats (e.g. RAW, ZLIB, GZIP). // Feature detection is not triggered while using RAW mode (i.e. we never // call crc32() with a NULL buffer). -#if defined(CRC32_ARMV8_CRC32) - arm_check_features(); -#elif defined(CRC32_SIMD_SSE42_PCLMUL) - x86_check_features(); +#if defined(CRC32_ARMV8_CRC32) || defined(CRC32_SIMD_SSE42_PCLMUL) + cpu_check_features(); #endif if (version == Z_NULL || version[0] != my_version[0] || @@ -306,10 +314,9 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; - if (x86_cpu_enable_simd) { + s->hash_bits = memLevel + 7; + if ((x86_cpu_enable_simd || arm_cpu_enable_crc32) && s->hash_bits < 15) { s->hash_bits = 15; - } else { - s->hash_bits = memLevel + 7; } s->hash_size = 1 << s->hash_bits; @@ -319,7 +326,14 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->window = (Bytef *) ZALLOC(strm, s->w_size + window_padding, 2*sizeof(Byte)); + /* Avoid use of unitialized values in the window, see crbug.com/1137613 and + * crbug.com/1144420 */ + zmemzero(s->window, (s->w_size + window_padding) * (2 * sizeof(Byte))); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + /* Avoid use of uninitialized value, see: + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11360 + */ + zmemzero(s->prev, s->w_size * sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->high_water = 0; /* nothing written to s->window yet */ @@ -531,7 +545,7 @@ int ZEXPORT deflateResetKeep (strm) s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); - s->last_flush = Z_NO_FLUSH; + s->last_flush = -2; _tr_init(s); @@ -624,12 +638,12 @@ int ZEXPORT deflateParams(strm, level, strategy) func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && - s->high_water) { + s->last_flush != -2) { /* Flush the last buffer: */ int err = deflate(strm, Z_BLOCK); if (err == Z_STREAM_ERROR) return err; - if (strm->avail_out == 0) + if (strm->avail_in || (s->strstart - s->block_start) + s->lookahead) return Z_BUF_ERROR; } if (s->level != level) { @@ -1211,7 +1225,7 @@ ZLIB_INTERNAL unsigned deflate_read_buf(strm, buf, size) #ifdef GZIP if (strm->state->wrap == 2) copy_with_crc(strm, buf, len); - else + else #endif { zmemcpy(buf, strm->next_in, len); @@ -1344,7 +1358,16 @@ local uInt longest_match(s, cur_match) * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ - Assert(scan[2] == match[2], "scan[2]?"); + if (!x86_cpu_enable_simd && !arm_cpu_enable_crc32) { + Assert(scan[2] == match[2], "scan[2]?"); + } else { + /* When using CRC hashing, scan[2] and match[2] may mismatch, but in + * that case at least one of the other hashed bytes will mismatch + * also. Bytes 0 and 1 were already checked above, and we know there + * are at least four bytes to check otherwise the mismatch would have + * been found by the scan_end comparison above, so: */ + Assert(scan[2] == match[2] || scan[3] != match[3], "scan[2]??"); + } scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && @@ -1375,7 +1398,16 @@ local uInt longest_match(s, cur_match) * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; - Assert(*scan == *match, "match[2]?"); + if (!x86_cpu_enable_simd && !arm_cpu_enable_crc32) { + Assert(*scan == *match, "match[2]?"); + } else { + /* When using CRC hashing, scan[2] and match[2] may mismatch, but in + * that case at least one of the other hashed bytes will mismatch + * also. Bytes 0 and 1 were already checked above, and we know there + * are at least four bytes to check otherwise the mismatch would have + * been found by the scan_end comparison above, so: */ + Assert(*scan == *match || scan[1] != match[1], "match[2]??"); + } /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. @@ -1519,11 +1551,12 @@ local void fill_window_c(deflate_state *s); local void fill_window(deflate_state *s) { +#ifdef DEFLATE_FILL_WINDOW_SSE2 if (x86_cpu_enable_simd) { fill_window_sse(s); return; } - +#endif fill_window_c(s); } @@ -1561,6 +1594,8 @@ local void fill_window_c(s) s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; + if (s->insert > s->strstart) + s->insert = s->strstart; slide_hash(s); more += wsize; } @@ -1790,6 +1825,7 @@ local block_state deflate_stored(s, flush) s->matches = 2; /* clear hash */ zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); s->strstart = s->w_size; + s->insert = s->strstart; } else { if (s->window_size - s->strstart <= used) { @@ -1798,12 +1834,14 @@ local block_state deflate_stored(s, flush) zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ + if (s->insert > s->strstart) + s->insert = s->strstart; } zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); s->strstart += used; + s->insert += MIN(used, s->w_size - s->insert); } s->block_start = s->strstart; - s->insert += MIN(used, s->w_size - s->insert); } if (s->high_water < s->strstart) s->high_water = s->strstart; @@ -1818,7 +1856,7 @@ local block_state deflate_stored(s, flush) return block_done; /* Fill the window with any remaining input. */ - have = s->window_size - s->strstart - 1; + have = s->window_size - s->strstart; if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { /* Slide the window down. */ s->block_start -= s->w_size; @@ -1827,12 +1865,15 @@ local block_state deflate_stored(s, flush) if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ have += s->w_size; /* more space now */ + if (s->insert > s->strstart) + s->insert = s->strstart; } if (have > s->strm->avail_in) have = s->strm->avail_in; if (have) { deflate_read_buf(s->strm, s->window + s->strstart, have); s->strstart += have; + s->insert += MIN(have, s->w_size - s->insert); } if (s->high_water < s->strstart) s->high_water = s->strstart; @@ -2035,7 +2076,13 @@ local block_state deflate_slow(s, flush) uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ - check_match(s, s->strstart-1, s->prev_match, s->prev_length); + if (s->prev_match == -1) { + /* The window has slid one byte past the previous match, + * so the first byte cannot be compared. */ + check_match(s, s->strstart, s->prev_match+1, s->prev_length-1); + } else { + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + } _tr_tally_dist(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); diff --git a/deps/zlib/fill_window_sse.c b/deps/zlib/fill_window_sse.c index e19154bc14d238..a841c99904baee 100644 --- a/deps/zlib/fill_window_sse.c +++ b/deps/zlib/fill_window_sse.c @@ -9,10 +9,10 @@ * For conditions of distribution and use, see copyright notice in zlib.h */ -#pragma GCC target ("sse2") -#include #include "deflate.h" +#ifdef DEFLATE_FILL_WINDOW_SSE2 + #define UPDATE_HASH(s,h,i) \ {\ if (s->level < 6) { \ @@ -29,6 +29,8 @@ extern int deflate_read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#include + void fill_window_sse(deflate_state *s) { const __m128i xmm_wsize = _mm_set1_epi16(s->w_size); @@ -176,3 +178,5 @@ void fill_window_sse(deflate_state *s) Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "not enough room for search"); } + +#endif /* DEFLATE_FILL_WINDOW_SSE2 */ diff --git a/deps/zlib/google/BUILD.gn b/deps/zlib/google/BUILD.gn index 4024836205f64c..e996b167dba856 100644 --- a/deps/zlib/google/BUILD.gn +++ b/deps/zlib/google/BUILD.gn @@ -7,6 +7,7 @@ import("//build_overrides/build.gni") if (build_with_chromium) { static_library("zip") { sources = [ + "redact.h", "zip.cc", "zip.h", "zip_internal.cc", @@ -18,6 +19,7 @@ if (build_with_chromium) { ] deps = [ "//base", + "//base:i18n", "//third_party/zlib:minizip", ] } @@ -28,10 +30,10 @@ if (build_with_chromium) { "compression_utils.h", ] deps = [ - ":compression_utils_portable", "//base", "//third_party/zlib", ] + public_deps = [ ":compression_utils_portable" ] } } @@ -42,7 +44,5 @@ static_library("compression_utils_portable") { "compression_utils_portable.cc", "compression_utils_portable.h", ] - deps = [ - "//third_party/zlib", - ] + public_deps = [ "//third_party/zlib" ] } diff --git a/deps/zlib/google/DEPS b/deps/zlib/google/DEPS index 144fbd149295b3..03f2cb950b9517 100644 --- a/deps/zlib/google/DEPS +++ b/deps/zlib/google/DEPS @@ -2,4 +2,5 @@ include_rules = [ '+base', '+build', '+testing', + "+third_party/zlib/zlib.h", ] diff --git a/deps/zlib/google/OWNERS b/deps/zlib/google/OWNERS index 1ca25314631124..411670ca13aa17 100644 --- a/deps/zlib/google/OWNERS +++ b/deps/zlib/google/OWNERS @@ -1,5 +1,8 @@ +fdegros@chromium.org +noel@chromium.org satorux@chromium.org # compression_utils* asvitkine@chromium.org isherman@chromium.org +cavalcantii@chromium.org diff --git a/deps/zlib/google/compression_utils.cc b/deps/zlib/google/compression_utils.cc index 9f63a840167d47..781c805d0a950b 100644 --- a/deps/zlib/google/compression_utils.cc +++ b/deps/zlib/google/compression_utils.cc @@ -5,16 +5,15 @@ #include "third_party/zlib/google/compression_utils.h" #include "base/bit_cast.h" -#include "base/logging.h" +#include "base/check_op.h" #include "base/process/memory.h" -#include "base/strings/string_piece.h" #include "base/sys_byteorder.h" #include "third_party/zlib/google/compression_utils_portable.h" namespace compression { -bool GzipCompress(base::StringPiece input, +bool GzipCompress(base::span input, char* output_buffer, size_t output_buffer_size, size_t* compressed_size, @@ -35,7 +34,11 @@ bool GzipCompress(base::StringPiece input, return true; } -bool GzipCompress(base::StringPiece input, std::string* output) { +bool GzipCompress(base::span input, std::string* output) { + return GzipCompress(base::as_bytes(input), output); +} + +bool GzipCompress(base::span input, std::string* output) { // Not using std::vector<> because allocation failures are recoverable, // which is hidden by std::vector<>. static_assert(sizeof(Bytef) == 1, ""); @@ -87,30 +90,44 @@ bool GzipUncompress(const std::string& input, std::string* output) { return false; } -bool GzipUncompress(base::StringPiece input, base::StringPiece output) { +bool GzipUncompress(base::span input, + base::span output) { + return GzipUncompress(base::as_bytes(input), base::as_bytes(output)); +} + +bool GzipUncompress(base::span input, + base::span output) { uLongf uncompressed_size = GetUncompressedSize(input); if (uncompressed_size > output.size()) return false; return zlib_internal::GzipUncompressHelper( bit_cast(output.data()), &uncompressed_size, bit_cast(input.data()), - static_cast(input.length())) == Z_OK; + static_cast(input.size())) == Z_OK; } -bool GzipUncompress(base::StringPiece input, std::string* output) { +bool GzipUncompress(base::span input, std::string* output) { + return GzipUncompress(base::as_bytes(input), output); +} + +bool GzipUncompress(base::span input, std::string* output) { // Disallow in-place usage, i.e., |input| using |*output| as underlying data. - DCHECK_NE(input.data(), output->data()); + DCHECK_NE(reinterpret_cast(input.data()), output->data()); uLongf uncompressed_size = GetUncompressedSize(input); output->resize(uncompressed_size); return zlib_internal::GzipUncompressHelper( bit_cast(output->data()), &uncompressed_size, bit_cast(input.data()), - static_cast(input.length())) == Z_OK; + static_cast(input.size())) == Z_OK; +} + +uint32_t GetUncompressedSize(base::span compressed_data) { + return GetUncompressedSize(base::as_bytes(compressed_data)); } -uint32_t GetUncompressedSize(base::StringPiece compressed_data) { +uint32_t GetUncompressedSize(base::span compressed_data) { return zlib_internal::GetGzipUncompressedSize( - bit_cast(compressed_data.data()), compressed_data.length()); + bit_cast(compressed_data.data()), compressed_data.size()); } } // namespace compression diff --git a/deps/zlib/google/compression_utils.h b/deps/zlib/google/compression_utils.h index 516220719ff43c..cca47be1efef6a 100644 --- a/deps/zlib/google/compression_utils.h +++ b/deps/zlib/google/compression_utils.h @@ -7,7 +7,7 @@ #include -#include "base/strings/string_piece.h" +#include "base/containers/span.h" namespace compression { @@ -18,7 +18,7 @@ namespace compression { // |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::StringPiece input, +bool GzipCompress(base::span input, char* output_buffer, size_t output_buffer_size, size_t* compressed_size, @@ -29,27 +29,41 @@ bool GzipCompress(base::StringPiece input, // |input| and |output| are allowed to point to the same string (in-place // operation). // Returns true for success. -bool GzipCompress(base::StringPiece input, std::string* output); +bool GzipCompress(base::span input, std::string* output); + +// Like the above method, but using uint8_t instead. +bool GzipCompress(base::span 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::StringPiece to avoid allocations if +// 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::StringPiece input, base::StringPiece output); +bool GzipUncompress(base::span input, + base::span output); + +// Like the above method, but using uint8_t instead. +bool GzipUncompress(base::span input, + base::span 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::StringPiece input, std::string* output); +bool GzipUncompress(base::span input, std::string* output); + +// Like the above method, but using uint8_t instead. +bool GzipUncompress(base::span input, std::string* output); // Returns the uncompressed size from GZIP-compressed |compressed_data|. -uint32_t GetUncompressedSize(base::StringPiece compressed_data); +uint32_t GetUncompressedSize(base::span compressed_data); + +// Like the above method, but using uint8_t instead. +uint32_t GetUncompressedSize(base::span compressed_data); } // namespace compression diff --git a/deps/zlib/google/compression_utils_portable.cc b/deps/zlib/google/compression_utils_portable.cc index 191e349e31ad30..331e41e1257253 100644 --- a/deps/zlib/google/compression_utils_portable.cc +++ b/deps/zlib/google/compression_utils_portable.cc @@ -5,7 +5,7 @@ * found in the Chromium source repository LICENSE file. */ -#include "third_party/zlib/google/compression_utils_portable.h" +#include "compression_utils_portable.h" #include #include @@ -84,7 +84,7 @@ int CompressHelper(WrapperType wrapper_type, int compression_level, void* (*malloc_fn)(size_t), void (*free_fn)(void*)) { - if (compression_level < 1 || compression_level > 9) { + if (compression_level < 0 || compression_level > 9) { compression_level = Z_DEFAULT_COMPRESSION; } diff --git a/deps/zlib/google/compression_utils_portable.h b/deps/zlib/google/compression_utils_portable.h index cd004e86cf515c..c1f377571fbba0 100644 --- a/deps/zlib/google/compression_utils_portable.h +++ b/deps/zlib/google/compression_utils_portable.h @@ -9,10 +9,14 @@ #include +/* TODO(cavalcantii): remove support for Chromium ever building with a system + * zlib. + */ #if defined(USE_SYSTEM_ZLIB) #include +/* AOSP build requires relative paths. */ #else -#include "third_party/zlib/zlib.h" +#include "zlib.h" #endif namespace zlib_internal { diff --git a/deps/zlib/google/compression_utils_unittest.cc b/deps/zlib/google/compression_utils_unittest.cc index b0e04b8c973633..76572e5a47eac4 100644 --- a/deps/zlib/google/compression_utils_unittest.cc +++ b/deps/zlib/google/compression_utils_unittest.cc @@ -7,10 +7,9 @@ #include #include +#include #include -#include "base/logging.h" -#include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace compression { @@ -34,37 +33,33 @@ const uint8_t kCompressedData[] = { } // namespace TEST(CompressionUtilsTest, GzipCompression) { - std::string data(reinterpret_cast(kData), base::size(kData)); + std::string data(reinterpret_cast(kData), std::size(kData)); std::string compressed_data; EXPECT_TRUE(GzipCompress(data, &compressed_data)); std::string golden_compressed_data( reinterpret_cast(kCompressedData), - base::size(kCompressedData)); + std::size(kCompressedData)); EXPECT_EQ(golden_compressed_data, compressed_data); } TEST(CompressionUtilsTest, GzipUncompression) { std::string compressed_data(reinterpret_cast(kCompressedData), - base::size(kCompressedData)); + std::size(kCompressedData)); std::string uncompressed_data; EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data)); std::string golden_data(reinterpret_cast(kData), - base::size(kData)); + std::size(kData)); EXPECT_EQ(golden_data, uncompressed_data); } -TEST(CompressionUtilsTest, GzipUncompressionFromStringPieceToString) { - base::StringPiece compressed_data( - reinterpret_cast(kCompressedData), - base::size(kCompressedData)); - +TEST(CompressionUtilsTest, GzipUncompressionFromSpanToString) { std::string uncompressed_data; - EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data)); + EXPECT_TRUE(GzipUncompress(kCompressedData, &uncompressed_data)); std::string golden_data(reinterpret_cast(kData), - base::size(kData)); + std::size(kData)); EXPECT_EQ(golden_data, uncompressed_data); } @@ -89,10 +84,10 @@ TEST(CompressionUtilsTest, LargeInput) { TEST(CompressionUtilsTest, InPlace) { const std::string original_data(reinterpret_cast(kData), - base::size(kData)); + std::size(kData)); const std::string golden_compressed_data( reinterpret_cast(kCompressedData), - base::size(kCompressedData)); + std::size(kCompressedData)); std::string data(original_data); EXPECT_TRUE(GzipCompress(data, &data)); diff --git a/deps/zlib/google/redact.h b/deps/zlib/google/redact.h new file mode 100644 index 00000000000000..ea7da16a52751c --- /dev/null +++ b/deps/zlib/google/redact.h @@ -0,0 +1,31 @@ +// Copyright (c) 2022 The Chromium Authors. All rights reserved. +// 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 + +#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_ diff --git a/deps/zlib/google/test/data/Different Encryptions.zip b/deps/zlib/google/test/data/Different Encryptions.zip new file mode 100644 index 0000000000000000000000000000000000000000..858e7c74b61ee656a677a0661105b561959379db GIT binary patch literal 1083 zcmWIWW@h1HW&nbdRWgAOp5E>h2eLt!7l@s6QWJ|pQY%XIN-9c1GBS%5fG97&L?JaV zxu~+BBsE2kD*$MMF*74WGQ%sN2|!H@CP1tJ#G(w`46ZPR3XZP9x`sv;Ad?tpvNJF- zIJz=2Ff3>1-=}j&=&)?E>}>}F+0+-6Z1FCyBRRviFo^W-=hk9tHN3f`ROgS-f-5TC zOBZZ@sC%dxVdGn7t4{2wh^`T1TY=Od{s-sO8+ zKCd>nezmgsMR;YSdQ;T?>q`_XxTgnB6*G$oHC~Fa^&`|)8=STpnVKQn3JhO?ljo!v zpK#a)?+CF~H!XUiXd(DL1)iJM`>red=%ogr#m(5bp$^0K25%T3&XY(7Q zl~geFKn#i}LzqS(5vh~%)kHurROL1 literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/Empty Dir Same Name As File.zip b/deps/zlib/google/test/data/Empty Dir Same Name As File.zip new file mode 100644 index 0000000000000000000000000000000000000000..e26c42d158ccc295a62a8c740fccf6be0ad27666 GIT binary patch literal 218 zcmWIWW@Zs#00Hfyf{<5#H~Dh`*&xgT#6_tEsfi`2DQ=lX#U%=9nK`KeDC&Vq;D8gz zf~nUJ@MdHZVa9DROjiRVhy~Mzt_!9LM1zcn00D--js`#`Oc&5HRyL3n6A2MH- F0RS0yBUk_c literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/Mixed Paths.zip b/deps/zlib/google/test/data/Mixed Paths.zip new file mode 100644 index 0000000000000000000000000000000000000000..881f498599744758d61c396ba943c940d7bc8f43 GIT binary patch literal 12814 zcmaKz3v?9K8OOs*AfiG+1fnn~AWF=Fys`4ygrJ0EAwha1DoYrWZ8q7k4+tVyJ&=k} zX$2KTPeHWSS6k01RIK(v5v`AE1=~_R6~VStdn(kUB7*k2Gqd;m{@=vvxt_gu|G#_h z%$>bEJHHs4)v0rz0`t$xr?WMKuYTs+=hACax=@*sNN2J&4T+4C$V_s)S`2`L4}VqB zkAkM0N2R1BQq}A_749-O>C9`fsA|VfPOYDO)rAC`(%bKZ@-@p-em@!2k$w+5yLi~0 zt~nr6&ZZKXmUU_xT&E_*vk`3wKlF!}V*Lp+rKf?-yqCG@a1ZJY?561E@&$#2o6^B_ zW6whbgVRe#eBOfsrgWlW4lRGS={1JGckR?-*=q?$F?i1nCmuA1Zc0b5S5B5-^kBC5 zE}VeqwM2!#_{;kn&!PTI>Fo7e0%;XLnEYUkoJNGh5fyFNc;er38gY@O*Er`f1Wwz)pXol#iLaJI3GQsZP(4OFrO7u z&Z1(v#2HZ)r*z^cZGVCFOXBBWm=x%ys05@r1#48y=W??bfZ#Y z7B=mQF@grfu3b93uNC=Mu~LRYZQ)V`QN`ccrOh zl8$PKh4KvnM(5XTwWVuz{8eIQxpc}oa{H4O%QxU?OZv=QEhiXDw7AYK7V@h&(j!Oz zalhGCO)>jNbyFhgI>&#w^>&MK6OMMxf+uI1ebAJyRB95*#jdm3azP!TRi7p9l~6R5 zOf@@`EELocdUkrjVYzD6Zd0Pp1Fy26XhV41N89^+gQjOncPcSAUFSAuouL-BEeJNf zv#=bd8c(}f=SGV~{W?PZKk2kbei-QsW~wDj1>={$f5(?FMyjbP?i5=h>emt4+Uw)$ zzH}s}n6b)Q>yq)Ncs4<2a_8DSB> zipY*dkG~_2t!!y7o^-CZOi)MY+yN!W&!=ND#ccZXnitatv@Vr)otrEv8xgTq^52ms&2UBeZf` z|J@hR5t?H5u+mg+5q{Y1tN>+BqX5IeAe{Yp$nl;G2^zmv2CQebd zJKaqtovxPls{s;Rv#&4dzU3ks(-d<6)!B5Sx!$R9(@lxycs7-G_C8=cXzxv&!TskJ z*UA}`HML|{@Q*0JipV|Bm)|IN(}-e%IznAu?{T5Lg3WeVl`c)B?ObgOf}1aYq`Um4 zwlSV0Hz9iTozHKRUw51{ufhXIOfzoPS$UT>aD? zSas~6+>=d*karYNKIOLQ@}ec#h`01|09EXFq*Ujpu~9cECe>rrxSf4S}H% z{U#jq^PXEyJG66{Vm=vx?+(R2L!6j_lPC=zqcTujFSST2*P|^cus(OI8k);n-Xb(`yW$*--e5 z3{RW)E!#7_#_-NM_f`3EiB`=XWz(jD@%~pP=czc~Zd3e9jBo7Nc(IC)mc5V0c=3Jb zJ*VP)^G&^v!T7U}c6&v|$I9NvV!Ytf4s%s}oQ#jd_`Unj8mi*sWqdrwyS}|=mx@o2 z@d+6J!H2)vtKt)7d?LoTPrjv}j90{Jc$rKE;{#H;+f73XE5xLT$?r+N)iab6~ht7Yo+M87r_=VhX}TBdup4LYIXyi62V%hb7TdtAkN znJBK7>F(W+_Em9SCW@Tuw6_;vQhbc4U#u-`cE{o7km9$rb zZ9#C=?jOAeQ;Q3)Q9+g1tPUOfbTYSA?VAwq4f@Y_%=3KTi)ci<5JpDPOCt<~8PTwI zAPg__VWS455z!rJ@4+4Xj$VTCz$4YpgF)jiv;on>YSrP>JqylED>t-`YvIB17K(ey z181kD2hZ+71L*#>`r!C}uYaA_{)6);ynJW??LZh9NUDXNn}-OkL713LgpnI~#M*^$ zzLoMM3pAj8h!2=0c*F-ntDuz#4{IV%3o*VM!HH=*!o-V)@a+g9+K(`D(nIJbm~4qYXIhUi6(M~V=n))93lc^a z5|Z~L$Y?{t%qC(2LxPBQB#i7M1ndZc+K({UfqZdZk07H32{U7eVLK8~!7pCzN0@3P ziscB7rS%9?4-u8OBRG=wBh0)-j8r2ymX;&T^(3ul)d)gbjxbb4kX0iHX*t5s%>;oK zK~Rek27f`IkRpg_EyCDwGs(OZK}c&6hK79QhzN54+Hi13IqE1%k^@hc)*PJQ?&V27AfQzT1F!jj zQ5G7~s)M19q+{~3LQ~pwFf~z7{xdb4q?Q~^wi4N{*1!(5*x(LcpbkPh17R&R7%rsW z|MXl8<63KQd^E*LZD3r>4UX4R+_xJ@XurY4dLjb9frvI7j66pOS&kqcT>9E`aQyES z7mfolZ8;blNWY+j;lRmh$HCZq!dP%1r4^6|nc7v%TQLHwQ(sF~TokW4wKv0Vf1`iVmN&`VHH5j~zew()=G7#4) zgYhMV=S@by1lOf@8O&@YrhR#VqiTKO_h5eU9HSoc_5vC0FPJH!C)CznAf){TPc=r6 zH4FBWNAH{cUfT;EYn{YGzkjs4;NkWG*WT``odvU9=?QJrvk-W2T3awNj)=N;2yHHy zaLpK=y1+5CykKH85%knopE@lt82GIZgm!A}Fqk;*6QLi>T4OLVh#oG{q>RSEl=S%R=w83Di zKA-}7ftdCejBU4Ady~}ig1NVe`&w0jgKJsAos6XCerzg`(yoH3dZGeXfrz#hj2Lt6 z`3eNIv0&f@0z7AdfVLJ4{FeaFTOgp#1p|ZV&sKbOfrOS9OjP;=-!9WzX4ljHf{9f= zK~KhU46QJjc+w~6;W!f7VleTxPtfymB(%t2qLBW$j*grjkt3m91``u~f}WBip>+lm z4L(5+%8}4UgNgfmf}WKlp``{B`-z~Z>tr>M&|ZUyPkbV@f?91bF@%&MjS*TwZ8w;x z_KDC6YQe$8?LHA&LG3u0c-kjIE2uRG6Nh~ww1V1nFmWFJDV47}@a$^Y!Ng>r2(6&@ x9ZWP4k=GqKiWVNsY$ry@4#c$XVC;~YoaZ~b(4(A!f>!$XIJI>%|I0(c{{Uhu%dY?c literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/Parent Dir Same Name As File.zip b/deps/zlib/google/test/data/Parent Dir Same Name As File.zip new file mode 100644 index 0000000000000000000000000000000000000000..76cfd906edcf69420bd0890af86cc65b4b9645f6 GIT binary patch literal 241 zcmWIWW@Zs#00Hfyf{<5#H~Dh`*&xgT#6_tEsfi`2DQ=lX#U%=9nK`KeDC*10j(z0@ zssmwuB=!2msmb|yDZyY8s*jOLgc-L@K+Ry#zzCvX_6B&P>O!{F*1oT<2Dasv;f0jM-U6ibYius UqiSPi1DV4Fgn2;P55!>r04Tf~v;Y7A literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/Repeated File Name With Different Cases.zip b/deps/zlib/google/test/data/Repeated File Name With Different Cases.zip new file mode 100644 index 0000000000000000000000000000000000000000..9eb44e1a507eb9762373b179f622840a7583a664 GIT binary patch literal 313 zcmWIWW@Zs#00Hfyf)J0^dM`d88-!Va*g3H{)i<*uHANvA$WkygGz>sdA9Uh*GA~da z2!qsvRQcqWrxrof8yTT0Z!=$Yod>84gu%u;2D^q96u^`l2Y53wi7?}K4p18yG%$iF xkmJyGpgRbno`Io((E!K;z4!*T^Qia$_5f;2Eu7Tx);P@0024OGW!4k literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/Repeated File Name.zip b/deps/zlib/google/test/data/Repeated File Name.zip new file mode 100644 index 0000000000000000000000000000000000000000..4a3e7b13c66a4a97893bcfa86f683f8cb833c19a GIT binary patch literal 227 zcmWIWW@Zs#00Hfyf{<5#H~Dh`*&xgT#6_tEsfi`2DQ=lX#U%=9nK`KeDC*10j(z0@ zssmwU^}(sh`FSZ&4U9}8%($%p>I8!ZMi2$FFu)sC7rI3dJq!#Dj0PyWSlK`_OhA|e Jq!U3L1_1j*E6e}@ literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/SJIS Bug 846195.zip b/deps/zlib/google/test/data/SJIS Bug 846195.zip new file mode 100644 index 0000000000000000000000000000000000000000..078300284e634ae6cc639ef7ab4f6942bd0d2e30 GIT binary patch literal 479 zcmWIWW@Zs#U|`??;`xkG+-L8n0$GJXEDyvJ!kV@=Eov@l_Gs>Ij@J+N@(hl*FgA6L zZ;sI`sVLz+ZOC`XfrsV5`Zbprc%3|Knzk%la`Q*cCf5%Of2cT}J)Zb$=S=lg`n$YY z=5u9Ud^yn~SNvezGMl2rTZ}#>!E+WT{3~%?E%7rc?N|trrB}Mq@Ix0Ba5E&oY@p& z%Mjqr$RxsmJA{BvU|?iW08v0dAiR(b!{!-~5fH-`VH!rjYXRP@Y#{wiK)4b}D=>n1 E06cr4rvLx| literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/Windows Special Names.zip b/deps/zlib/google/test/data/Windows Special Names.zip new file mode 100644 index 0000000000000000000000000000000000000000..4fac907fa754b07364376c9a9e1eb894331fa789 GIT binary patch literal 3985 zcmZ{nSx8i27>38C%{JRDB2QA(MHty?&??l0<_w_`v@z6FlY_#jWqZ+blMzK5byEZ_ zgf~HvW(5(2WfxI5EjQi;Ezpfn6!re+KQrI|y+@TF&pE!&`TjYt&ZDu)y!^2d^U-x` zZ_U&t4|k2gu~`GKRwdJoO*K1{jb5^GvFD$~6!5sX_0BK^&C0{7uC2<(aje62t>ZQh zN64&v8}{6^r{k&XZI38k>c&fTd`k4odw1^`@2R{;!0Kg3R~BL$W(~62DQ#+zmx~*i zb3LW#@Y~yC5H_nI1I5#&JNBuqHTjn`TCna+Y7`K&1_x+pV0dD6qI=tD#LXI#!NOb^ zto!ox9nJHY^DpRaZt3Xlp?O}I>jV7IzWgvf8i1jR_L832Jnq|ZRYh}3bM{|HV!GB3hk|<5-7hZQnn|4ae3>7{@yNx3%D*8(vYnK6_^z z>+p@OiEj6ItRlK*PBv{$bbMl2s+QtqZoEv#&*avXP<*Z%pR41uE{*J@_&hg0PsclM z4Sr4W`EGo^jz@ZP)>3?d8(*N~7hepVPVt3qe4&mH`qX}(;)~q)A{{^b^~p1em%H(D z9ly5pK#?1-iq%L>=0wLQH8dZkxYUF=)zrEr-Ai$)32~}v_N(zbDK0f3PBpdv9#u?n zsR?naDerF4Ns3ENh*M1$t``5KxYUF=)s%noTAboi6XI0Uxd*q#Q(S67oNAhRaMFp9 zcqcMzAXY_Hw4!pF^y;7Lst!K5*`p@J9pP5tBvzS|J2tO>MPrQraU{ol2^6gnmb3*T zks~>CaELc5p24CPV6z2aYCVEoEyQU{z`dGaT?^3}%m^VYY(c&ULwsH9IV@~}O7O(| zclW^6nqylFw<+MPaW%!V7V3P4>I-943--ohfo}CMHnmXsx&BMD-sUIIo)&CL7VB%1 zENS6d43|-~-Xlv|pf*7LDp}uzr7Sne#UGtwWKFS?g(?RXI9VfXWkGfU@x818HnRYi z0Qhd!0NYuB=Ky>^Yk&kZ~vNLPYV&X1Pq`FR<#g^ECC5< zf^98Cn&uyoI<7 z0u~81$o>}Mr6ocQvciSvwM3{vwzv?}p)Twy)F6voh#E_T8f2FXam*5-23hAq+_OZe zK{mP&J(dVH$Wj-g5dQ{RtLp~Y>q0C8VJ*+Cv)F}6n<>882VjSh$P9db#I@^Kk;s2* CY2@hu literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/Wrong CRC.zip b/deps/zlib/google/test/data/Wrong CRC.zip new file mode 100644 index 0000000000000000000000000000000000000000..ee9a1ef05431f98929214db4fcdca68724dc121d GIT binary patch literal 231 zcmWIWW@h1H00EvDw_xK1r}E8#Y!K#UkYRAnFDfc6C`nDxE2$_64dG;9p8hT-aTXAl zR&X;gvS=u>Ft7y2Gctr^WELx=W#*(RWF!_VB&DY2DI{kk=B1~mC?uwpq!uY;mJ};E z2RSR01ErJma|=pAhH?dXGcw6Bo^T*C@+4Vp^=yjj^miWz}07)aNF GI1B(~IW;Q) literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/empty.zip b/deps/zlib/google/test/data/empty.zip new file mode 100644 index 0000000000000000000000000000000000000000..15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7 GIT binary patch literal 22 NcmWIWW@Tf*000g10H*)| literal 0 HcmV?d00001 diff --git a/deps/zlib/google/test/data/test_posix_permissions.zip b/deps/zlib/google/test/data/test_posix_permissions.zip new file mode 100644 index 0000000000000000000000000000000000000000..a058ba17445c37a6a55f8d2b99cf8ef0aad4e036 GIT binary patch literal 574 zcmWIWW@h1H0D+cVtq?E+O0Y7>Fc|2SRFs5IHQ&}+5?S(h6`mzJ!z~1 k8U+m_%8XjlxEE*?G`w&b#mWX$!@vTBI*beqe?S}t00Hb}KmY&$ literal 0 HcmV?d00001 diff --git a/deps/zlib/google/zip.cc b/deps/zlib/google/zip.cc index 907e5da72e974d..1a43196e99d5af 100644 --- a/deps/zlib/google/zip.cc +++ b/deps/zlib/google/zip.cc @@ -4,17 +4,18 @@ #include "third_party/zlib/google/zip.h" -#include #include #include #include "base/bind.h" #include "base/files/file.h" #include "base/files/file_enumerator.h" +#include "base/files/file_util.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" @@ -26,18 +27,13 @@ bool IsHiddenFile(const base::FilePath& file_path) { return file_path.BaseName().value()[0] == '.'; } -bool ExcludeNoFilesFilter(const base::FilePath& file_path) { - return true; -} - -bool ExcludeHiddenFilesFilter(const base::FilePath& file_path) { - return !IsHiddenFile(file_path); -} - // Creates a directory at |extract_dir|/|entry_path|, including any parents. bool CreateDirectory(const base::FilePath& extract_dir, const base::FilePath& entry_path) { - return base::CreateDirectory(extract_dir.Append(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|. @@ -50,222 +46,226 @@ std::unique_ptr CreateFilePathWriterDelegate( class DirectFileAccessor : public FileAccessor { public: - explicit DirectFileAccessor(base::FilePath src_dir) : src_dir_(src_dir) {} + explicit DirectFileAccessor(base::FilePath src_dir) + : src_dir_(std::move(src_dir)) {} + ~DirectFileAccessor() override = default; - std::vector OpenFilesForReading( - const std::vector& paths) override { - std::vector files; - for (const auto& path : paths) { - base::File file; - if (base::PathExists(path) && !base::DirectoryExists(path)) { - file = base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ); + bool Open(const Paths paths, std::vector* 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()); } - files.push_back(std::move(file)); } - return files; - } - bool DirectoryExists(const base::FilePath& file) override { - return base::DirectoryExists(file); + return true; } - std::vector ListDirectoryContent( - const base::FilePath& dir) override { - std::vector files; + bool List(const base::FilePath& path, + std::vector* const files, + std::vector* const subdirs) override { + DCHECK(!path.IsAbsolute()); + DCHECK(files); + DCHECK(subdirs); + base::FileEnumerator file_enumerator( - dir, false /* recursive */, + src_dir_.Append(path), false /* recursive */, base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); - for (base::FilePath path = file_enumerator.Next(); !path.value().empty(); - path = file_enumerator.Next()) { - files.push_back(DirectoryContentEntry(path, base::DirectoryExists(path))); + + while (!file_enumerator.Next().empty()) { + const base::FileEnumerator::FileInfo info = file_enumerator.GetInfo(); + (info.IsDirectory() ? subdirs : files) + ->push_back(path.Append(info.GetName())); } - return files; + + return true; } - base::Time GetLastModifiedTime(const base::FilePath& path) override { + bool GetInfo(const base::FilePath& path, Info* const info) override { + DCHECK(!path.IsAbsolute()); + DCHECK(info); + base::File::Info file_info; - if (!base::GetFileInfo(path, &file_info)) { - LOG(ERROR) << "Failed to retrieve file modification time for " - << path.value(); + if (!base::GetFileInfo(src_dir_.Append(path), &file_info)) { + PLOG(ERROR) << "Cannot get info of " << Redact(path); + return false; } - return file_info.last_modified; + + info->is_directory = file_info.is_directory; + info->last_modified = file_info.last_modified; + + return true; } private: - base::FilePath src_dir_; - - DISALLOW_COPY_AND_ASSIGN(DirectFileAccessor); + const base::FilePath src_dir_; }; } // namespace -ZipParams::ZipParams(const base::FilePath& src_dir, - const base::FilePath& dest_file) - : src_dir_(src_dir), - dest_file_(dest_file), - file_accessor_(new DirectFileAccessor(src_dir)) {} - -#if defined(OS_POSIX) -// Does not take ownership of |fd|. -ZipParams::ZipParams(const base::FilePath& src_dir, int dest_fd) - : src_dir_(src_dir), - dest_fd_(dest_fd), - file_accessor_(new DirectFileAccessor(src_dir)) {} -#endif +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) { - // Using a pointer to avoid copies of a potentially large array. - const std::vector* files_to_add = ¶ms.files_to_zip(); - std::vector all_files; - if (files_to_add->empty()) { - // Include all files from the src_dir (modulo the src_dir itself and - // filtered and hidden files). - - files_to_add = &all_files; - // Using a list so we can call push_back while iterating. - std::list entries; - entries.push_back(FileAccessor::DirectoryContentEntry( - params.src_dir(), true /* is directory*/)); - const FilterCallback& filter_callback = params.filter_callback(); - for (auto iter = entries.begin(); iter != entries.end(); ++iter) { - const base::FilePath& entry_path = iter->path; - if (iter != entries.begin() && // Don't filter the root dir. - ((!params.include_hidden_files() && IsHiddenFile(entry_path)) || - (filter_callback && !filter_callback.Run(entry_path)))) { - continue; - } - - if (iter != entries.begin()) { // Exclude the root dir from the ZIP file. - // Make the path relative for AddEntryToZip. - base::FilePath relative_path; - bool success = - params.src_dir().AppendRelativePath(entry_path, &relative_path); - DCHECK(success); - all_files.push_back(relative_path); - } - - if (iter->is_directory) { - std::vector subentries = - params.file_accessor()->ListDirectoryContent(entry_path); - entries.insert(entries.end(), subentries.begin(), subentries.end()); - } - } - } + DirectFileAccessor default_accessor(params.src_dir); + FileAccessor* const file_accessor = params.file_accessor ?: &default_accessor; std::unique_ptr zip_writer; -#if defined(OS_POSIX) - if (params.dest_fd() != base::kInvalidPlatformFile) { - DCHECK(params.dest_file().empty()); - zip_writer = internal::ZipWriter::CreateWithFd( - params.dest_fd(), params.src_dir(), params.file_accessor()); + +#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(), params.src_dir(), params.file_accessor()); + zip_writer = internal::ZipWriter::Create(params.dest_file, file_accessor); if (!zip_writer) return false; } - return zip_writer->WriteEntries(*files_to_add); -} -bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) { - return UnzipWithFilterCallback( - src_file, dest_dir, base::BindRepeating(&ExcludeNoFilesFilter), true); + 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))); + }, + ¶ms)); + + 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 UnzipWithFilterCallback(const base::FilePath& src_file, - const base::FilePath& dest_dir, - const FilterCallback& filter_cb, - bool log_skipped_files) { +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()) { - DLOG(WARNING) << "Failed to open " << src_file.value(); + PLOG(ERROR) << "Cannot open " << Redact(src_file) << ": " + << base::File::ErrorToString(file.error_details()); return false; } - return UnzipWithFilterAndWriters( - file.GetPlatformFile(), - base::BindRepeating(&CreateFilePathWriterDelegate, dest_dir), - base::BindRepeating(&CreateDirectory, dest_dir), filter_cb, - log_skipped_files); + + 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 UnzipWithFilterAndWriters(const base::PlatformFile& src_file, - const WriterFactory& writer_factory, - const DirectoryCreator& directory_creator, - const FilterCallback& filter_cb, - bool log_skipped_files) { +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)) { - DLOG(WARNING) << "Failed to open src_file " << src_file; + LOG(ERROR) << "Cannot open ZIP from file handle " << src_file; return false; } - while (reader.HasMore()) { - if (!reader.OpenCurrentEntryInZip()) { - DLOG(WARNING) << "Failed to open the current file in zip"; - 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; } - const base::FilePath& entry_path = reader.current_entry_info()->file_path(); - if (reader.current_entry_info()->is_unsafe()) { - DLOG(WARNING) << "Found an unsafe file in zip " << entry_path; - return false; + + if (options.filter && !options.filter.Run(entry->path)) { + VLOG(1) << "Skipped ZIP entry " << Redact(entry->path); + continue; } - if (filter_cb.Run(entry_path)) { - if (reader.current_entry_info()->is_directory()) { - if (!directory_creator.Run(entry_path)) - return false; - } else { - std::unique_ptr writer = writer_factory.Run(entry_path); - if (!reader.ExtractCurrentEntry(writer.get(), - std::numeric_limits::max())) { - DLOG(WARNING) << "Failed to extract " << entry_path; + + 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; - } } - } else if (log_skipped_files) { - DLOG(WARNING) << "Skipped file " << entry_path; + + continue; } - if (!reader.AdvanceToNextEntry()) { - DLOG(WARNING) << "Failed to advance to the next file"; - return false; + // It's a file. + std::unique_ptr writer = writer_factory.Run(entry->path); + if (!writer || !reader.ExtractCurrentEntry(writer.get())) { + LOG(ERROR) << "Cannot extract file " << Redact(entry->path) + << " from ZIP"; + if (!options.continue_on_error) + return false; } } - return true; + + return reader.ok(); } bool ZipWithFilterCallback(const base::FilePath& src_dir, const base::FilePath& dest_file, - const FilterCallback& filter_cb) { + FilterCallback filter) { DCHECK(base::DirectoryExists(src_dir)); - ZipParams params(src_dir, dest_file); - params.set_filter_callback(filter_cb); - return Zip(params); + 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 Zip(const base::FilePath& src_dir, + const base::FilePath& dest_file, bool include_hidden_files) { - if (include_hidden_files) { - return ZipWithFilterCallback(src_dir, dest_file, - base::BindRepeating(&ExcludeNoFilesFilter)); - } else { - return ZipWithFilterCallback( - src_dir, dest_file, base::BindRepeating(&ExcludeHiddenFilesFilter)); - } + return Zip({.src_dir = src_dir, + .dest_file = dest_file, + .include_hidden_files = include_hidden_files}); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) bool ZipFiles(const base::FilePath& src_dir, - const std::vector& src_relative_paths, + Paths src_relative_paths, int dest_fd) { DCHECK(base::DirectoryExists(src_dir)); - ZipParams params(src_dir, dest_fd); - params.set_files_to_zip(src_relative_paths); - return Zip(params); + return Zip({.src_dir = src_dir, + .dest_fd = dest_fd, + .src_files = src_relative_paths}); } -#endif // defined(OS_POSIX) +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) } // namespace zip diff --git a/deps/zlib/google/zip.h b/deps/zlib/google/zip.h index 4f64a8aca883a8..25ec655caf314b 100644 --- a/deps/zlib/google/zip.h +++ b/deps/zlib/google/zip.h @@ -5,9 +5,13 @@ #ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_H_ #define THIRD_PARTY_ZLIB_GOOGLE_ZIP_H_ +#include +#include +#include #include #include "base/callback.h" +#include "base/containers/span.h" #include "base/files/file_path.h" #include "base/files/platform_file.h" #include "base/time/time.h" @@ -21,99 +25,118 @@ namespace zip { class WriterDelegate; +// Paths passed as span to avoid copying them. +using Paths = base::span; + // 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. -// If none is provided, the files are accessed directly. -// All parameters paths are expected to be absolute. +// +// All parameters paths are expected to be relative to the source directory. class FileAccessor { public: virtual ~FileAccessor() = default; - struct DirectoryContentEntry { - DirectoryContentEntry(const base::FilePath& path, bool is_directory) - : path(path), is_directory(is_directory) {} - base::FilePath path; + struct Info { bool is_directory = false; + base::Time last_modified; }; // Opens files specified in |paths|. // Directories should be mapped to invalid files. - virtual std::vector OpenFilesForReading( - const std::vector& paths) = 0; + virtual bool Open(Paths paths, std::vector* files) = 0; + + // Lists contents of a directory at |path|. + virtual bool List(const base::FilePath& path, + std::vector* files, + std::vector* subdirs) = 0; - virtual bool DirectoryExists(const base::FilePath& path) = 0; - virtual std::vector ListDirectoryContent( - const base::FilePath& dir_path) = 0; - virtual base::Time GetLastModifiedTime(const base::FilePath& path) = 0; + // Gets info about a file or directory. + virtual bool GetInfo(const base::FilePath& path, Info* info) = 0; }; -class ZipParams { - public: - ZipParams(const base::FilePath& src_dir, const base::FilePath& dest_file); -#if defined(OS_POSIX) - // Does not take ownership of |dest_fd|. - ZipParams(const base::FilePath& src_dir, int dest_fd); +// Progress of a ZIP creation operation. +struct Progress { + // Total number of bytes read from files getting zipped so far. + std::int64_t bytes = 0; - int dest_fd() const { return dest_fd_; } -#endif + // Number of file entries added to the ZIP so far. + // A file entry is added after its bytes have been processed. + int files = 0; - const base::FilePath& src_dir() const { return src_dir_; } - - const base::FilePath& dest_file() const { return dest_file_; } - - // Restricts the files actually zipped to the paths listed in - // |src_relative_paths|. They must be relative to the |src_dir| passed in the - // constructor 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. - void set_files_to_zip(const std::vector& src_relative_paths) { - src_files_ = src_relative_paths; - } - const std::vector& files_to_zip() const { return src_files_; } - - using FilterCallback = base::RepeatingCallback; - void set_filter_callback(FilterCallback filter_callback) { - filter_callback_ = filter_callback; - } - const FilterCallback& filter_callback() const { return filter_callback_; } - - void set_include_hidden_files(bool include_hidden_files) { - include_hidden_files_ = include_hidden_files; - } - bool include_hidden_files() const { return include_hidden_files_; } - - // Sets a custom file accessor for file operations. Default is to directly - // access the files (with fopen and the rest). - // Useful in cases where running in a sandbox process and file access has to - // go through IPC, for example. - void set_file_accessor(std::unique_ptr file_accessor) { - file_accessor_ = std::move(file_accessor); - } - FileAccessor* file_accessor() const { return file_accessor_.get(); } - - private: - base::FilePath src_dir_; - - base::FilePath dest_file_; -#if defined(OS_POSIX) - int dest_fd_ = base::kInvalidPlatformFile; -#endif + // 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; + +using FilterCallback = base::RepeatingCallback; - // The relative paths to the files that should be included in the zip file. If - // this is empty, all files in |src_dir_| are included. - std::vector src_files_; +// ZIP creation parameters and options. +struct ZipParams { + // Source directory. Ignored if |file_accessor| is set. + base::FilePath src_dir; - // Filter used to exclude files from the ZIP file. Only effective when - // |src_files_| is empty. - FilterCallback filter_callback_; + // 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 - // Whether hidden files should be included in the ZIP file. Only effective - // when |src_files_| is empty. - bool include_hidden_files_ = true; + // Destination file path. + // Either dest_file or dest_fd should be set, but not both. + base::FilePath dest_file; - // Abstraction around file system access used to read files. An implementation - // that accesses files directly is provided by default. - std::unique_ptr file_accessor_; +#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 @@ -125,56 +148,65 @@ bool Zip(const ZipParams& params); // 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. -using FilterCallback = base::RepeatingCallback; bool ZipWithFilterCallback(const base::FilePath& src_dir, const base::FilePath& dest_file, - const FilterCallback& filter_cb); + 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 Zip(const base::FilePath& src_dir, + const base::FilePath& dest_file, bool include_hidden_files); -#if defined(OS_POSIX) +#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, - const std::vector& src_relative_paths, + Paths src_relative_paths, int dest_fd); -#endif // defined(OS_POSIX) +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) + +// 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; + + // Password to decrypt the encrypted files. + std::string password; + + // Should ignore errors when extracting files? + bool continue_on_error = false; +}; -// Unzip the contents of zip_file into dest_dir. -// For each file in zip_file, include it only if the callback |filter_cb| -// returns true. Otherwise omit it. -// If |log_skipped_files| is true, files skipped during extraction are printed -// to debug log. -using FilterCallback = base::RepeatingCallback; -bool UnzipWithFilterCallback(const base::FilePath& zip_file, - const base::FilePath& dest_dir, - const FilterCallback& filter_cb, - bool log_skipped_files); - -// Unzip the contents of zip_file, using the writers provided by writer_factory. -// For each file in zip_file, include it only if the callback |filter_cb| -// returns true. Otherwise omit it. -// If |log_skipped_files| is true, files skipped during extraction are printed -// to debug log. typedef base::RepeatingCallback( const base::FilePath&)> WriterFactory; + typedef base::RepeatingCallback DirectoryCreator; -bool UnzipWithFilterAndWriters(const base::PlatformFile& zip_file, - const WriterFactory& writer_factory, - const DirectoryCreator& directory_creator, - const FilterCallback& filter_cb, - bool log_skipped_files); - -// Unzip the contents of zip_file into dest_dir. -bool Unzip(const base::FilePath& zip_file, const base::FilePath& dest_dir); + +// 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 diff --git a/deps/zlib/google/zip_internal.cc b/deps/zlib/google/zip_internal.cc index 314740f5447c03..1adf2e6d0e8fef 100644 --- a/deps/zlib/google/zip_internal.cc +++ b/deps/zlib/google/zip_internal.cc @@ -5,10 +5,16 @@ #include "third_party/zlib/google/zip_internal.h" #include +#include #include +#include +#include "base/files/file_path.h" #include "base/logging.h" +#include "base/no_destructor.h" +#include "base/notreached.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #if defined(USE_SYSTEM_MINIZIP) @@ -34,9 +40,9 @@ typedef struct { } WIN32FILE_IOWIN; // This function is derived from third_party/minizip/iowin32.c. -// Its only difference is that it treats the char* as UTF8 and +// Its only difference is that it treats the filename as UTF-8 and // uses the Unicode version of CreateFile. -void* ZipOpenFunc(void *opaque, const char* filename, int mode) { +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; @@ -54,10 +60,11 @@ void* ZipOpenFunc(void *opaque, const char* filename, int mode) { creation_disposition = CREATE_ALWAYS; } - base::string16 filename16 = base::UTF8ToUTF16(filename); - if ((filename != NULL) && (desired_access != 0)) { - file = CreateFile(filename16.c_str(), desired_access, share_mode, - NULL, creation_disposition, flags_and_attributes, NULL); + if (filename != nullptr && desired_access != 0) { + file = CreateFileW( + base::UTF8ToWide(static_cast(filename)).c_str(), + desired_access, share_mode, nullptr, creation_disposition, + flags_and_attributes, nullptr); } if (file == INVALID_HANDLE_VALUE) @@ -77,11 +84,11 @@ void* ZipOpenFunc(void *opaque, const char* filename, int mode) { } #endif -#if defined(OS_POSIX) +#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 char* filename, int mode) { +void* FdOpenFileFunc(void* opaque, const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; @@ -103,15 +110,15 @@ void* FdOpenFileFunc(void* opaque, const char* filename, int mode) { int FdCloseFileFunc(void* opaque, void* stream) { fclose(static_cast(stream)); - free(opaque); // malloc'ed in FillFdOpenFileFunc() + 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_filefunc_def* pzlib_filefunc_def, int fd) { - fill_fopen_filefunc(pzlib_filefunc_def); - pzlib_filefunc_def->zopen_file = FdOpenFileFunc; +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(malloc(sizeof(fd))); *ptr_fd = fd; @@ -122,7 +129,7 @@ void FillFdOpenFileFunc(zlib_filefunc_def* pzlib_filefunc_def, int fd) { #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 char* filename, int mode) { +void* HandleOpenFileFunc(void* opaque, const void* /*filename*/, int mode) { WIN32FILE_IOWIN file_ret; file_ret.hf = static_cast(opaque); file_ret.error = 0; @@ -136,7 +143,7 @@ void* HandleOpenFileFunc(void* opaque, const char* filename, int mode) { } int HandleCloseFileFunc(void* opaque, void* stream) { - free(stream); // malloc'ed in HandleOpenFileFunc() + free(stream); // malloc'ed in HandleOpenFileFunc() return 0; } #endif @@ -146,8 +153,8 @@ int HandleCloseFileFunc(void* opaque, void* stream) { // expect their opaque parameters refer to this struct. struct ZipBuffer { const char* data; // weak - size_t length; - size_t offset; + ZPOS64_T length; + ZPOS64_T offset; }; // Opens the specified file. When this function returns a non-NULL pointer, zlib @@ -156,7 +163,7 @@ struct ZipBuffer { // 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 char* /*filename*/, int mode) { +void* OpenZipBuffer(void* opaque, const void* /*filename*/, int mode) { if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) { NOTREACHED(); return NULL; @@ -173,10 +180,11 @@ void* OpenZipBuffer(void* opaque, const char* /*filename*/, int mode) { uLong ReadZipBuffer(void* opaque, void* /*stream*/, void* buf, uLong size) { ZipBuffer* buffer = static_cast(opaque); DCHECK_LE(buffer->offset, buffer->length); - size_t remaining_bytes = buffer->length - buffer->offset; + ZPOS64_T remaining_bytes = buffer->length - buffer->offset; if (!buffer || !buffer->data || !remaining_bytes) return 0; - size = std::min(size, static_cast(remaining_bytes)); + if (size > remaining_bytes) + size = remaining_bytes; memcpy(buf, &buffer->data[buffer->offset], size); buffer->offset += size; return size; @@ -193,21 +201,23 @@ uLong WriteZipBuffer(void* /*opaque*/, } // Returns the offset from the beginning of the data. -long GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) { +ZPOS64_T GetOffsetOfZipBuffer(void* opaque, void* /*stream*/) { ZipBuffer* buffer = static_cast(opaque); if (!buffer) return -1; - return static_cast(buffer->offset); + return buffer->offset; } // Moves the current offset to the specified position. -long SeekZipBuffer(void* opaque, void* /*stream*/, uLong offset, int origin) { +long SeekZipBuffer(void* opaque, + void* /*stream*/, + ZPOS64_T offset, + int origin) { ZipBuffer* buffer = static_cast(opaque); if (!buffer) return -1; if (origin == ZLIB_FILEFUNC_SEEK_CUR) { - buffer->offset = std::min(buffer->offset + static_cast(offset), - buffer->length); + buffer->offset = std::min(buffer->offset + offset, buffer->length); return 0; } if (origin == ZLIB_FILEFUNC_SEEK_END) { @@ -215,7 +225,7 @@ long SeekZipBuffer(void* opaque, void* /*stream*/, uLong offset, int origin) { return 0; } if (origin == ZLIB_FILEFUNC_SEEK_SET) { - buffer->offset = std::min(buffer->length, static_cast(offset)); + buffer->offset = std::min(buffer->length, offset); return 0; } NOTREACHED(); @@ -241,7 +251,7 @@ int GetErrorOfZipBuffer(void* /*opaque*/, void* /*stream*/) { // 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.LocalExplode(&file_time_parts); + file_time.UTCExplode(&file_time_parts); zip_fileinfo zip_info = {}; if (file_time_parts.year >= 1980) { @@ -266,33 +276,33 @@ namespace zip { namespace internal { unzFile OpenForUnzipping(const std::string& file_name_utf8) { - zlib_filefunc_def* zip_func_ptrs = NULL; + zlib_filefunc64_def* zip_func_ptrs = nullptr; #if defined(OS_WIN) - zlib_filefunc_def zip_funcs; - fill_win32_filefunc(&zip_funcs); - zip_funcs.zopen_file = ZipOpenFunc; + zlib_filefunc64_def zip_funcs; + fill_win32_filefunc64(&zip_funcs); + zip_funcs.zopen64_file = ZipOpenFunc; zip_func_ptrs = &zip_funcs; #endif - return unzOpen2(file_name_utf8.c_str(), zip_func_ptrs); + return unzOpen2_64(file_name_utf8.c_str(), zip_func_ptrs); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) unzFile OpenFdForUnzipping(int zip_fd) { - zlib_filefunc_def zip_funcs; + zlib_filefunc64_def zip_funcs; FillFdOpenFileFunc(&zip_funcs, zip_fd); // Passing dummy "fd" filename to zlib. - return unzOpen2("fd", &zip_funcs); + return unzOpen2_64("fd", &zip_funcs); } #endif #if defined(OS_WIN) unzFile OpenHandleForUnzipping(HANDLE zip_handle) { - zlib_filefunc_def zip_funcs; - fill_win32_filefunc(&zip_funcs); - zip_funcs.zopen_file = HandleOpenFileFunc; + 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("fd", &zip_funcs); + return unzOpen2_64("fd", &zip_funcs); } #endif @@ -308,72 +318,152 @@ unzFile PrepareMemoryForUnzipping(const std::string& data) { buffer->length = data.length(); buffer->offset = 0; - zlib_filefunc_def zip_functions; - zip_functions.zopen_file = OpenZipBuffer; + zlib_filefunc64_def zip_functions; + zip_functions.zopen64_file = OpenZipBuffer; zip_functions.zread_file = ReadZipBuffer; zip_functions.zwrite_file = WriteZipBuffer; - zip_functions.ztell_file = GetOffsetOfZipBuffer; - zip_functions.zseek_file = SeekZipBuffer; + zip_functions.ztell64_file = GetOffsetOfZipBuffer; + zip_functions.zseek64_file = SeekZipBuffer; zip_functions.zclose_file = CloseZipBuffer; zip_functions.zerror_file = GetErrorOfZipBuffer; - zip_functions.opaque = static_cast(buffer); - return unzOpen2(NULL, &zip_functions); + zip_functions.opaque = buffer; + return unzOpen2_64(nullptr, &zip_functions); } zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag) { - zlib_filefunc_def* zip_func_ptrs = NULL; + zlib_filefunc64_def* zip_func_ptrs = nullptr; #if defined(OS_WIN) - zlib_filefunc_def zip_funcs; - fill_win32_filefunc(&zip_funcs); - zip_funcs.zopen_file = ZipOpenFunc; + zlib_filefunc64_def zip_funcs; + fill_win32_filefunc64(&zip_funcs); + zip_funcs.zopen64_file = ZipOpenFunc; zip_func_ptrs = &zip_funcs; #endif - return zipOpen2(file_name_utf8.c_str(), - append_flag, - NULL, // global comment - zip_func_ptrs); + return zipOpen2_64(file_name_utf8.c_str(), append_flag, nullptr, + zip_func_ptrs); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) zipFile OpenFdForZipping(int zip_fd, int append_flag) { - zlib_filefunc_def zip_funcs; + zlib_filefunc64_def zip_funcs; FillFdOpenFileFunc(&zip_funcs, zip_fd); // Passing dummy "fd" filename to zlib. - return zipOpen2("fd", append_flag, NULL, &zip_funcs); + 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) { + 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; - zip_fileinfo file_info = TimeToZipFileInfo(last_modified_time); - if (ZIP_OK != zipOpenNewFileInZip4(zip_file, // file - str_path.c_str(), // filename - &file_info, // zip_fileinfo - NULL, // extrafield_local, - 0u, // size_extrafield_local - NULL, // extrafield_global - 0u, // size_extrafield_global - NULL, // comment - Z_DEFLATED, // method - Z_DEFAULT_COMPRESSION, // level - 0, // raw - -MAX_WBITS, // windowBits - DEF_MEM_LEVEL, // memLevel - Z_DEFAULT_STRATEGY, // strategy - NULL, // password - 0, // crcForCrypting - 0, // versionMadeBy - LANGUAGE_ENCODING_FLAG)) { // flagBase - DLOG(ERROR) << "Could not open zip file entry " << str_path; + 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; + + using StringPiece = base::FilePath::StringPieceType; + + // Skip the leading dot. + StringPiece 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 const base::NoDestructor< + std::unordered_set>> + exts(std::initializer_list{ + 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 (exts->count(ext_without_dot)) + return kStored; + + return kDeflated; +} + } // namespace internal } // namespace zip diff --git a/deps/zlib/google/zip_internal.h b/deps/zlib/google/zip_internal.h index 49fb902a7408e7..92833fa1702130 100644 --- a/deps/zlib/google/zip_internal.h +++ b/deps/zlib/google/zip_internal.h @@ -35,7 +35,7 @@ namespace internal { // Windows. unzFile OpenForUnzipping(const std::string& file_name_utf8); -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) // Opens the file referred to by |zip_fd| for unzipping. unzFile OpenFdForUnzipping(int zip_fd); #endif @@ -54,16 +54,30 @@ unzFile PrepareMemoryForUnzipping(const std::string& data); // Windows. |append_flag| will be passed to zipOpen2(). zipFile OpenForZipping(const std::string& file_name_utf8, int append_flag); -#if defined(OS_POSIX) +#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 -// Wrapper around zipOpenNewFileInZip4 which passes most common options. +// 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); + 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; diff --git a/deps/zlib/google/zip_reader.cc b/deps/zlib/google/zip_reader.cc index 8b4bb4aee782bf..2cc101c75ce7c9 100644 --- a/deps/zlib/google/zip_reader.cc +++ b/deps/zlib/google/zip_reader.cc @@ -4,17 +4,23 @@ #include "third_party/zlib/google/zip_reader.h" +#include #include #include "base/bind.h" +#include "base/check.h" #include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/i18n/icu_string_conversions.h" #include "base/logging.h" -#include "base/macros.h" -#include "base/single_thread_task_runner.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/strcat.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/threading/sequenced_task_runner_handle.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) @@ -26,114 +32,93 @@ #endif // defined(OS_WIN) #endif // defined(USE_SYSTEM_MINIZIP) -namespace zip { +#if defined(OS_POSIX) +#include +#endif +namespace zip { namespace { -// StringWriterDelegate -------------------------------------------------------- +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(error); + } +#undef SWITCH_ERR +} -// A writer delegate that writes no more than |max_read_bytes| to a given -// std::string. -class StringWriterDelegate : public WriterDelegate { - public: - StringWriterDelegate(size_t max_read_bytes, std::string* output); - ~StringWriterDelegate() override; +bool IsValidFileNameCharacterOnWindows(char16_t c) { + if (c < 32) + return false; - // WriterDelegate methods: + 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; + } - // Returns true. - bool PrepareOutput() override; + return true; +} - // Appends |num_bytes| bytes from |data| to the output string. Returns false - // if |num_bytes| will cause the string to exceed |max_read_bytes|. - bool WriteBytes(const char* data, int num_bytes) override; +// A writer delegate that writes to a given string. +class StringWriterDelegate : public WriterDelegate { + public: + explicit StringWriterDelegate(std::string* output) : output_(output) {} - void SetTimeModified(const base::Time& time) override; + // WriterDelegate methods: + bool WriteBytes(const char* data, int num_bytes) override { + output_->append(data, num_bytes); + return true; + } private: - size_t max_read_bytes_; - std::string* output_; - - DISALLOW_COPY_AND_ASSIGN(StringWriterDelegate); + std::string* const output_; }; -StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes, - std::string* output) - : max_read_bytes_(max_read_bytes), - output_(output) { -} - -StringWriterDelegate::~StringWriterDelegate() { -} - -bool StringWriterDelegate::PrepareOutput() { - return true; -} - -bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) { - if (output_->size() + num_bytes > max_read_bytes_) - return false; - output_->append(data, num_bytes); - return true; -} - -void StringWriterDelegate::SetTimeModified(const base::Time& time) { - // Do nothing. +#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 -// TODO(satorux): The implementation assumes that file names in zip files -// are encoded in UTF-8. This is true for zip files created by Zip() -// function in zip.h, but not true for user-supplied random zip files. -ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip, - const unz_file_info& raw_file_info) - : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)), - is_directory_(false), - is_unsafe_(false), - is_encrypted_(false) { - original_size_ = raw_file_info.uncompressed_size; - - // Directory entries in zip files end with "/". - is_directory_ = base::EndsWith(file_name_in_zip, "/", - base::CompareCase::INSENSITIVE_ASCII); - - // Check the file name here for directory traversal issues. - is_unsafe_ = file_path_.ReferencesParent(); - - // We also consider that the file name is unsafe, if it's invalid UTF-8. - base::string16 file_name_utf16; - if (!base::UTF8ToUTF16(file_name_in_zip.data(), file_name_in_zip.size(), - &file_name_utf16)) { - is_unsafe_ = true; - } - - // We also consider that the file name is unsafe, if it's absolute. - // On Windows, IsAbsolute() returns false for paths starting with "/". - if (file_path_.IsAbsolute() || - base::StartsWith(file_name_in_zip, "/", - base::CompareCase::INSENSITIVE_ASCII)) - is_unsafe_ = true; - - // Whether the file is encrypted is bit 0 of the flag. - is_encrypted_ = raw_file_info.flag & 1; - - // Construct the last modified time. The timezone info is not present in - // zip files, so we construct the time as local time. - base::Time::Exploded exploded_time = {}; // Zero-clear. - exploded_time.year = raw_file_info.tmu_date.tm_year; - // The month in zip file is 0-based, whereas ours is 1-based. - exploded_time.month = raw_file_info.tmu_date.tm_mon + 1; - exploded_time.day_of_month = raw_file_info.tmu_date.tm_mday; - exploded_time.hour = raw_file_info.tmu_date.tm_hour; - exploded_time.minute = raw_file_info.tmu_date.tm_min; - exploded_time.second = raw_file_info.tmu_date.tm_sec; - exploded_time.millisecond = 0; - - if (!base::Time::FromLocalExploded(exploded_time, &last_modified_)) - last_modified_ = base::Time::UnixEpoch(); -} - ZipReader::ZipReader() { Reset(); } @@ -142,13 +127,14 @@ ZipReader::~ZipReader() { Close(); } -bool ZipReader::Open(const base::FilePath& zip_file_path) { +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_file_path.AsUTF8Unsafe()); + zip_file_ = internal::OpenForUnzipping(zip_path.AsUTF8Unsafe()); if (!zip_file_) { + LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path); return false; } @@ -158,12 +144,13 @@ bool ZipReader::Open(const base::FilePath& zip_file_path) { bool ZipReader::OpenFromPlatformFile(base::PlatformFile zip_fd) { DCHECK(!zip_file_); -#if defined(OS_POSIX) +#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; } @@ -179,107 +166,242 @@ bool ZipReader::OpenFromString(const std::string& data) { void ZipReader::Close() { if (zip_file_) { - unzClose(zip_file_); + if (const UnzipError err{unzClose(zip_file_)}; err != UNZ_OK) { + LOG(ERROR) << "Error while closing ZIP archive: " << err; + } } Reset(); } -bool ZipReader::HasMore() { - return !reached_end_; -} - -bool ZipReader::AdvanceToNextEntry() { +const ZipReader::Entry* ZipReader::Next() { DCHECK(zip_file_); - // Should not go further if we already reached the end. if (reached_end_) - return false; + return nullptr; - unz_file_pos position = {}; - if (unzGetFilePos(zip_file_, &position) != UNZ_OK) - return false; - const int current_entry_index = position.num_of_file; - // If we are currently at the last entry, then the next position is the - // end of the zip file, so mark that we reached the end. - if (current_entry_index + 1 == num_entries_) { - reached_end_ = true; - } else { - DCHECK_LT(current_entry_index + 1, num_entries_); - if (unzGoToNextFile(zip_file_) != UNZ_OK) { - return false; + 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; } } - current_entry_info_.reset(); - return true; + + next_index_++; + + if (!OpenEntry()) { + reached_end_ = true; + ok_ = false; + return nullptr; + } + + return &entry_; } -bool ZipReader::OpenCurrentEntryInZip() { +bool ZipReader::OpenEntry() { DCHECK(zip_file_); - unz_file_info raw_file_info = {}; - char raw_file_name_in_zip[internal::kZipMaxPath] = {}; - const int result = unzGetCurrentFileInfo(zip_file_, - &raw_file_info, - raw_file_name_in_zip, - sizeof(raw_file_name_in_zip) - 1, - NULL, // extraField. - 0, // extraFieldBufferSize. - NULL, // szComment. - 0); // commentBufferSize. - if (result != UNZ_OK) + // 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; - if (raw_file_name_in_zip[0] == '\0') + } + + 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; - current_entry_info_.reset( - new EntryInfo(raw_file_name_in_zip, raw_file_info)); + } + + // 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; + + // Construct the last modified time. The timezone info is not present in ZIP + // archives, so we construct the time as UTC. + base::Time::Exploded exploded_time = {}; + exploded_time.year = info.tmu_date.tm_year; + exploded_time.month = info.tmu_date.tm_mon + 1; // 0-based vs 1-based + exploded_time.day_of_month = info.tmu_date.tm_mday; + exploded_time.hour = info.tmu_date.tm_hour; + exploded_time.minute = info.tmu_date.tm_min; + exploded_time.second = info.tmu_date.tm_sec; + exploded_time.millisecond = 0; + + 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(base::StringPiece16 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 base::StringPiece16::size_type i = in.find_first_not_of(u'/'); + if (i == base::StringPiece16::npos) + break; + + in.remove_prefix(i); + DCHECK(!in.empty()); + + // Isolate next path component. + const base::StringPiece16 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; +} + bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate, uint64_t num_bytes_to_extract) const { DCHECK(zip_file_); - - const int open_result = unzOpenCurrentFile(zip_file_); - if (open_result != UNZ_OK) + 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; - std::unique_ptr buf(new char[internal::kZipBufSize]); 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.get(), internal::kZipBufSize); + unzReadCurrentFile(zip_file_, buf, internal::kZipBufSize); if (num_bytes_read == 0) { entire_file_extracted = true; break; - } else if (num_bytes_read < 0) { - // If num_bytes_read < 0, then it's a specific UNZ_* error code. + } + + if (num_bytes_read < 0) { + LOG(ERROR) << "Cannot read file " << Redact(entry_.path) + << " from ZIP: " << UnzipError(num_bytes_read); break; - } else if (num_bytes_read > 0) { - uint64_t num_bytes_to_write = std::min( - remaining_capacity, base::checked_cast(num_bytes_read)); - if (!delegate->WriteBytes(buf.get(), num_bytes_to_write)) - break; - if (remaining_capacity == base::checked_cast(num_bytes_read)) { - // Ensures function returns true if the entire file has been read. - entire_file_extracted = - (unzReadCurrentFile(zip_file_, buf.get(), 1) == 0); - } - CHECK_GE(remaining_capacity, num_bytes_to_write); - remaining_capacity -= num_bytes_to_write; } + + DCHECK_LT(0, num_bytes_read); + CHECK_LE(num_bytes_read, internal::kZipBufSize); + + uint64_t num_bytes_to_write = std::min( + remaining_capacity, base::checked_cast(num_bytes_read)); + if (!delegate->WriteBytes(buf, num_bytes_to_write)) + break; + + if (remaining_capacity == base::checked_cast(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; } - unzCloseCurrentFile(zip_file_); + 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 && - current_entry_info()->last_modified() != base::Time::UnixEpoch()) { - delegate->SetTimeModified(current_entry_info()->last_modified()); + if (entire_file_extracted) { + delegate->SetPosixFilePermissions(entry_.posix_mode); + if (entry_.last_modified != base::Time::UnixEpoch()) { + delegate->SetTimeModified(entry_.last_modified); + } + } else { + delegate->OnError(); } return entire_file_extracted; @@ -289,35 +411,43 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( const base::FilePath& output_file_path, SuccessCallback success_callback, FailureCallback failure_callback, - const ProgressCallback& progress_callback) { + ProgressCallback progress_callback) { DCHECK(zip_file_); - DCHECK(current_entry_info_.get()); + DCHECK_LT(0, next_index_); + DCHECK(ok_); + DCHECK(!reached_end_); // If this is a directory, just create it and return. - if (current_entry_info()->is_directory()) { + if (entry_.is_directory) { if (base::CreateDirectory(output_file_path)) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(success_callback)); } else { - DVLOG(1) << "Unzip failed: unable to create directory."; - base::ThreadTaskRunnerHandle::Get()->PostTask( + LOG(ERROR) << "Cannot create directory " << Redact(output_file_path); + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(failure_callback)); } return; } - if (unzOpenCurrentFile(zip_file_) != UNZ_OK) { - DVLOG(1) << "Unzip failed: unable to open current zip entry."; - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - std::move(failure_callback)); + // 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::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(failure_callback)); return; } base::FilePath output_dir_path = output_file_path.DirName(); if (!base::CreateDirectory(output_dir_path)) { - DVLOG(1) << "Unzip failed: unable to create containing directory."; - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - std::move(failure_callback)); + LOG(ERROR) << "Cannot create directory " << Redact(output_dir_path); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(failure_callback)); return; } @@ -325,139 +455,154 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( base::File output_file(output_file_path, flags); if (!output_file.IsValid()) { - DVLOG(1) << "Unzip failed: unable to create platform file at " - << output_file_path.value(); - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - std::move(failure_callback)); + LOG(ERROR) << "Cannot create file " << Redact(output_file_path); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(failure_callback)); return; } - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(), - Passed(std::move(output_file)), - std::move(success_callback), std::move(failure_callback), - progress_callback, 0 /* initial offset */)); + 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) { - output->clear(); + if (max_read_bytes == 0) return true; - } - if (current_entry_info()->is_directory()) { - output->clear(); + 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. - std::string contents; - contents.reserve( - static_cast(std::min(base::checked_cast(max_read_bytes), - current_entry_info()->original_size()))); - - StringWriterDelegate writer(max_read_bytes, &contents); - if (!ExtractCurrentEntry(&writer, max_read_bytes)) { - if (contents.length() < max_read_bytes) { - // There was an error in extracting entry. If ExtractCurrentEntry() - // returns false, the entire file was not read - in which case - // contents.length() should equal |max_read_bytes| unless an error - // occurred which caused extraction to be aborted. - output->clear(); - } else { - // |num_bytes| is less than the length of current entry. - output->swap(contents); - } - return false; - } - output->swap(contents); - 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(std::min( + max_read_bytes, base::checked_cast(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 (unzGetGlobalInfo(zip_file_, &zip_info) != UNZ_OK) { + 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; - if (num_entries_ < 0) - return false; - // We are already at the end if the zip file is empty. - reached_end_ = (num_entries_ == 0); + num_entries_ = zip_info.number_entry; + reached_end_ = (num_entries_ <= 0); + ok_ = true; return true; } void ZipReader::Reset() { - zip_file_ = NULL; + zip_file_ = nullptr; num_entries_ = 0; - reached_end_ = false; - current_entry_info_.reset(); + next_index_ = 0; + reached_end_ = true; + ok_ = false; + entry_ = {}; } void ZipReader::ExtractChunk(base::File output_file, SuccessCallback success_callback, FailureCallback failure_callback, - const ProgressCallback& progress_callback, - const int64_t offset) { + ProgressCallback progress_callback, + int64_t offset) { char buffer[internal::kZipBufSize]; - const int num_bytes_read = unzReadCurrentFile(zip_file_, - buffer, - internal::kZipBufSize); + const int num_bytes_read = + unzReadCurrentFile(zip_file_, buffer, internal::kZipBufSize); if (num_bytes_read == 0) { - unzCloseCurrentFile(zip_file_); - std::move(success_callback).Run(); - } else if (num_bytes_read < 0) { - DVLOG(1) << "Unzip failed: error while reading zipfile " - << "(" << num_bytes_read << ")"; - std::move(failure_callback).Run(); - } else { - if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) { - DVLOG(1) << "Unzip failed: unable to write all bytes to target."; + 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; } - int64_t current_progress = offset + num_bytes_read; + std::move(success_callback).Run(); + return; + } - progress_callback.Run(current_progress); + 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; + } - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(), - Passed(std::move(output_file)), - std::move(success_callback), std::move(failure_callback), - progress_callback, current_progress)); + 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::SequencedTaskRunnerHandle::Get()->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) {} - -FileWriterDelegate::FileWriterDelegate(std::unique_ptr file) - : file_(file.get()), owned_file_(std::move(file)) {} +FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) { + DCHECK(file_); +} -FileWriterDelegate::~FileWriterDelegate() { - if (!file_->SetLength(file_length_)) { - DVPLOG(1) << "Failed updating length of written file"; - } +FileWriterDelegate::FileWriterDelegate(base::File owned_file) + : owned_file_(std::move(owned_file)) { + DCHECK_EQ(file_, &owned_file_); } +FileWriterDelegate::~FileWriterDelegate() {} + bool FileWriterDelegate::PrepareOutput() { - return file_->Seek(base::File::FROM_BEGIN, 0) >= 0; + 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) { @@ -471,32 +616,65 @@ 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( - const base::FilePath& output_file_path) - : output_file_path_(output_file_path) {} +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 (!base::CreateDirectory(output_file_path_.DirName())) + if (const base::FilePath dir = output_file_path_.DirName(); + !base::CreateDirectory(dir)) { + PLOG(ERROR) << "Cannot create directory " << Redact(dir); return false; + } - file_.Initialize(output_file_path_, - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); - return file_.IsValid(); -} + 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; + } -bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) { - return num_bytes == file_.WriteAtCurrentPos(data, num_bytes); + return true; } -void FilePathWriterDelegate::SetTimeModified(const base::Time& time) { - file_.Close(); - base::TouchFile(output_file_path_, base::Time::Now(), time); +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 diff --git a/deps/zlib/google/zip_reader.h b/deps/zlib/google/zip_reader.h index d442d428591e50..703b74be038f7a 100644 --- a/deps/zlib/google/zip_reader.h +++ b/deps/zlib/google/zip_reader.h @@ -7,15 +7,15 @@ #include #include +#include #include #include #include "base/callback.h" #include "base/files/file.h" #include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/numerics/safe_conversions.h" #include "base/time/time.h" #if defined(USE_SYSTEM_MINIZIP) @@ -34,33 +34,47 @@ class 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() = 0; + 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) = 0; + 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) = 0; + 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 files. A typical use case of this -// class is to scan entries in a zip file and extract them. The code will -// look like: +// 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; -// reader.Open(zip_file_path); -// while (reader.HasMore()) { -// reader.OpenCurrentEntryInZip(); -// const base::FilePath& entry_path = -// reader.current_entry_info()->file_path(); -// auto writer = CreateFilePathWriterDelegate(extract_dir, entry_path); -// reader.ExtractCurrentEntry(writer, std::numeric_limits::max()); -// reader.AdvanceToNextEntry(); +// if (!reader.Open(zip_path)) { +// // Cannot open +// return; // } // -// For simplicity, error checking is omitted in the example code above. The -// production code should check return values from all of these functions. +// 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: @@ -72,62 +86,70 @@ class ZipReader { // of bytes that have been processed so far. using ProgressCallback = base::RepeatingCallback; - // This class represents information of an entry (file or directory) in - // a zip file. - class EntryInfo { - public: - EntryInfo(const std::string& filename_in_zip, - const unz_file_info& raw_file_info); - - // Returns the file path. The path is usually relative like - // "foo/bar.txt", but if it's absolute, is_unsafe() returns true. - const base::FilePath& file_path() const { return file_path_; } - - // Returns the size of the original file (i.e. after uncompressed). - // Returns 0 if the entry is a directory. - // Note: 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() const { return original_size_; } - - // Returns the last modified time. If the time stored in the zip file was - // not valid, the unix epoch will be returned. + // 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. // - // The time 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 time stamp has a 2 second resolution. - // * there's no timezone information, so the time is interpreted as local. - base::Time last_modified() const { return last_modified_; } - - // Returns true if the entry is a directory. - bool is_directory() const { return is_directory_; } - - // Returns true if the entry is unsafe, like having ".." or invalid - // UTF-8 characters in its file name, or the file path is absolute. - bool is_unsafe() const { return is_unsafe_; } - - // Returns true if the entry is encrypted. - bool is_encrypted() const { return is_encrypted_; } - - private: - const base::FilePath file_path_; - int64_t original_size_; - base::Time last_modified_; - bool is_directory_; - bool is_unsafe_; - bool is_encrypted_; - DISALLOW_COPY_AND_ASSIGN(EntryInfo); + // * 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; + + // 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; + + // True if the file content is encrypted. + bool is_encrypted; + + // Entry POSIX permissions (POSIX systems only). + int posix_mode; }; ZipReader(); + + ZipReader(const ZipReader&) = delete; + ZipReader& operator=(const ZipReader&) = delete; + ~ZipReader(); - // Opens the zip file specified by |zip_file_path|. Returns true on + // Opens the ZIP archive specified by |zip_path|. Returns true on // success. - bool Open(const base::FilePath& zip_file_path); + bool Open(const base::FilePath& zip_path); - // Opens the zip file referred to by the platform file |zip_fd|, without + // 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); @@ -136,72 +158,94 @@ class ZipReader { // string until it finishes extracting files. bool OpenFromString(const std::string& data); - // Closes the currently opened zip file. This function is called in the + // 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(); - // Returns true if there is at least one entry to read. This function is - // used to scan entries with AdvanceToNextEntry(), like: - // - // while (reader.HasMore()) { - // // Do something with the current file here. - // reader.AdvanceToNextEntry(); - // } - bool HasMore(); + // 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); } - // Advances the next entry. Returns true on success. - bool AdvanceToNextEntry(); + // 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); } - // Opens the current entry in the zip file. On success, returns true and - // updates the the current entry state (i.e. current_entry_info() is - // updated). This function should be called before operations over the - // current entry like ExtractCurrentEntryToFile(). + // 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(). // - // Note that there is no CloseCurrentEntryInZip(). The the current entry - // state is reset automatically as needed. - bool OpenCurrentEntryInZip(); + // 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. Return value specifies whether - // the entire file was extracted. + // 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) const; + uint64_t num_bytes_to_extract = + std::numeric_limits::max()) 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. OpenCurrentEntryInZip() must be called beforehand. - // success_callback will be called on success and failure_callback will be - // called on failure. progress_callback will be called at least once. + // 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, - const ProgressCallback& progress_callback); + ProgressCallback progress_callback); // Extracts the current entry into memory. If the current entry is a - // directory, the |output| parameter is set to the empty string. If the - // current entry is a file, the |output| parameter is filled with its - // contents. OpenCurrentEntryInZip() must be called beforehand. Note: the - // |output| parameter can be filled with a big amount of data, avoid passing - // it around by value, but by reference or pointer. Note: the value returned - // by EntryInfo::original_size() cannot be trusted, so the real size of the - // uncompressed contents can be different. |max_read_bytes| limits the ammount - // of memory used to carry the entry. Returns true if the entire content is - // read. If the entry is bigger than |max_read_bytes|, returns false and - // |output| is filled with |max_read_bytes| of data. If an error occurs, - // returns false, and |output| is set to the empty string. + // 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; - // Returns the current entry info. Returns NULL if the current entry is - // not yet opened. OpenCurrentEntryInZip() must be called beforehand. - EntryInfo* current_entry_info() const { - return current_entry_info_.get(); + bool ExtractCurrentEntryToString(std::string* output) const { + return ExtractCurrentEntryToString( + base::checked_cast(output->max_size()), output); } - // Returns the number of entries in the zip file. - // Open() must be called beforehand. + // 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: @@ -211,25 +255,39 @@ class ZipReader { // 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(base::StringPiece16 in); + // 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, - const ProgressCallback& progress_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_; - std::unique_ptr current_entry_info_; + bool ok_; + Entry entry_; base::WeakPtrFactory weak_ptr_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(ZipReader); }; -// A writer delegate that writes to a given File. +// 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 @@ -238,14 +296,14 @@ class FileWriterDelegate : public WriterDelegate { explicit FileWriterDelegate(base::File* file); // Constructs a FileWriterDelegate that takes ownership of |file|. - explicit FileWriterDelegate(std::unique_ptr file); + explicit FileWriterDelegate(base::File owned_file); - // Truncates the file to the number of bytes written. - ~FileWriterDelegate() override; + FileWriterDelegate(const FileWriterDelegate&) = delete; + FileWriterDelegate& operator=(const FileWriterDelegate&) = delete; - // WriterDelegate methods: + ~FileWriterDelegate() override; - // Seeks to the beginning of the file, returning false if the seek fails. + // 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 @@ -255,45 +313,48 @@ class FileWriterDelegate : public WriterDelegate { // Sets the last-modified time of the data. void SetTimeModified(const base::Time& time) override; - // Return the actual size of the file. - int64_t file_length() { return file_length_; } + // On POSIX systems, sets the file to be executable if the source file was + // executable. + void SetPosixFilePermissions(int mode) override; - private: - // The file the delegate modifies. - base::File* file_; + // 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_. - std::unique_ptr owned_file_; + base::File owned_file_; - int64_t file_length_ = 0; + // The file the delegate modifies. + base::File* const file_ = &owned_file_; - DISALLOW_COPY_AND_ASSIGN(FileWriterDelegate); + int64_t file_length_ = 0; }; -// A writer delegate that writes a file at a given path. -class FilePathWriterDelegate : public WriterDelegate { +// 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(const base::FilePath& output_file_path); - ~FilePathWriterDelegate() override; + explicit FilePathWriterDelegate(base::FilePath output_file_path); - // WriterDelegate methods: + FilePathWriterDelegate(const FilePathWriterDelegate&) = delete; + FilePathWriterDelegate& operator=(const FilePathWriterDelegate&) = delete; - // Creates the output file and any necessary intermediate directories. - bool PrepareOutput() override; + ~FilePathWriterDelegate() override; - // Writes |num_bytes| bytes of |data| to the file, returning false if not all - // bytes could be written. - bool WriteBytes(const char* data, int num_bytes) 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; - // Sets the last-modified time of the data. - void SetTimeModified(const base::Time& time) override; + // Deletes the output file. + void OnError() override; private: - base::FilePath output_file_path_; - base::File file_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWriterDelegate); + const base::FilePath output_file_path_; }; } // namespace zip diff --git a/deps/zlib/google/zip_reader_unittest.cc b/deps/zlib/google/zip_reader_unittest.cc index 87190c7e0dee76..31dceaccad30de 100644 --- a/deps/zlib/google/zip_reader_unittest.cc +++ b/deps/zlib/google/zip_reader_unittest.cc @@ -8,29 +8,36 @@ #include #include -#include +#include #include +#include #include "base/bind.h" +#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/hash/md5.h" -#include "base/logging.h" #include "base/path_service.h" #include "base/run_loop.h" -#include "base/stl_util.h" +#include "base/strings/string_piece.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/zlib/google/zip_internal.h" -using ::testing::Return; using ::testing::_; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Return; +using ::testing::SizeIs; namespace { @@ -38,10 +45,7 @@ const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6"; class FileWrapper { public: - typedef enum { - READ_ONLY, - READ_WRITE - } AccessMode; + typedef enum { READ_ONLY, READ_WRITE } AccessMode; FileWrapper(const base::FilePath& path, AccessMode mode) { int flags = base::File::FLAG_READ; @@ -72,18 +76,13 @@ class MockUnzipListener : public base::SupportsWeakPtr { : success_calls_(0), failure_calls_(0), progress_calls_(0), - current_progress_(0) { - } + current_progress_(0) {} // Success callback for async functions. - void OnUnzipSuccess() { - success_calls_++; - } + void OnUnzipSuccess() { success_calls_++; } // Failure callback for async functions. - void OnUnzipFailure() { - failure_calls_++; - } + void OnUnzipFailure() { failure_calls_++; } // Progress callback for async functions. void OnUnzipProgress(int64_t progress) { @@ -110,184 +109,189 @@ class MockWriterDelegate : public zip::WriterDelegate { 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, - std::numeric_limits::max()); + return reader->ExtractCurrentEntry(&writer); } -bool LocateAndOpenEntry(zip::ZipReader* reader, - const base::FilePath& path_in_zip) { +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 (reader->HasMore()) { - if (!reader->OpenCurrentEntryInZip()) - return false; - if (reader->current_entry_info()->file_path() == path_in_zip) - return true; - reader->AdvanceToNextEntry(); + while (const zip::ZipReader::Entry* const entry = reader->Next()) { + EXPECT_TRUE(reader->ok()); + if (entry->path == path_in_zip) + return entry; } - return false; + + EXPECT_TRUE(reader->ok()); + return nullptr; } -} // namespace +using Paths = std::vector; + +} // namespace namespace zip { // Make the test a PlatformTest to setup autorelease pools properly on Mac. class ZipReaderTest : public PlatformTest { protected: - virtual void SetUp() { + void SetUp() override { PlatformTest::SetUp(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); test_dir_ = temp_dir_.GetPath(); - - ASSERT_TRUE(GetTestDataDirectory(&test_data_dir_)); - - test_zip_file_ = test_data_dir_.AppendASCII("test.zip"); - encrypted_zip_file_ = test_data_dir_.AppendASCII("test_encrypted.zip"); - evil_zip_file_ = test_data_dir_.AppendASCII("evil.zip"); - evil_via_invalid_utf8_zip_file_ = test_data_dir_.AppendASCII( - "evil_via_invalid_utf8.zip"); - evil_via_absolute_file_name_zip_file_ = test_data_dir_.AppendASCII( - "evil_via_absolute_file_name.zip"); - - test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/"))); - test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/bar/"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar.txt"))); - test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo.txt"))); - test_zip_contents_.insert( - base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden"))); } - virtual void TearDown() { - PlatformTest::TearDown(); + static base::FilePath GetTestDataDirectory() { + base::FilePath path; + CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &path)); + return path.AppendASCII("third_party") + .AppendASCII("zlib") + .AppendASCII("google") + .AppendASCII("test") + .AppendASCII("data"); } - bool GetTestDataDirectory(base::FilePath* path) { - bool success = base::PathService::Get(base::DIR_SOURCE_ROOT, path); - EXPECT_TRUE(success); - if (!success) - return false; - *path = path->AppendASCII("third_party"); - *path = path->AppendASCII("zlib"); - *path = path->AppendASCII("google"); - *path = path->AppendASCII("test"); - *path = path->AppendASCII("data"); - return true; - } + static Paths GetPaths(const base::FilePath& zip_path, + base::StringPiece 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()); + } - bool CompareFileAndMD5(const base::FilePath& path, - const std::string expected_md5) { - // Read the output file and compute the MD5. - std::string output; - if (!base::ReadFileToString(path, &output)) - return false; - const std::string md5 = base::MD5String(output); - return expected_md5 == md5; + 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. - base::FilePath test_data_dir_; + const base::FilePath data_dir_ = GetTestDataDirectory(); // The path to test.zip in the test data directory. - base::FilePath test_zip_file_; - // The path to test_encrypted.zip in the test data directory. - base::FilePath encrypted_zip_file_; - // The path to evil.zip in the test data directory. - base::FilePath evil_zip_file_; - // The path to evil_via_invalid_utf8.zip in the test data directory. - base::FilePath evil_via_invalid_utf8_zip_file_; - // The path to evil_via_absolute_file_name.zip in the test data directory. - base::FilePath evil_via_absolute_file_name_zip_file_; - std::set test_zip_contents_; - + 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; - ASSERT_TRUE(reader.Open(test_zip_file_)); + 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); - ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); + EXPECT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); + EXPECT_TRUE(reader.ok()); } TEST_F(ZipReaderTest, Open_NonExistentFile) { ZipReader reader; - ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("nonexistent.zip"))); + EXPECT_FALSE(reader.ok()); + EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("nonexistent.zip"))); + EXPECT_FALSE(reader.ok()); } TEST_F(ZipReaderTest, Open_ExistentButNonZipFile) { ZipReader reader; - ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("create_test_zip.sh"))); + EXPECT_FALSE(reader.ok()); + EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("create_test_zip.sh"))); + EXPECT_FALSE(reader.ok()); } -// Iterate through the contents in the test zip file, and compare that the -// contents collected from the zip reader matches the expected contents. +TEST_F(ZipReaderTest, Open_EmptyFile) { + ZipReader reader; + EXPECT_FALSE(reader.ok()); + EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("empty.zip"))); + EXPECT_FALSE(reader.ok()); +} + +// 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) { - std::set actual_contents; + Paths actual_contents; ZipReader reader; - ASSERT_TRUE(reader.Open(test_zip_file_)); - while (reader.HasMore()) { - ASSERT_TRUE(reader.OpenCurrentEntryInZip()); - actual_contents.insert(reader.current_entry_info()->file_path()); - ASSERT_TRUE(reader.AdvanceToNextEntry()); + 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_FALSE(reader.AdvanceToNextEntry()); // Shouldn't go further. - EXPECT_EQ(test_zip_contents_.size(), - static_cast(reader.num_entries())); - EXPECT_EQ(test_zip_contents_.size(), actual_contents.size()); - EXPECT_EQ(test_zip_contents_, actual_contents); + + 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 file from a file descriptor, iterate through its contents, -// and compare that they match the expected 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) { - std::set actual_contents; + Paths actual_contents; ZipReader reader; FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); - ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); - while (reader.HasMore()) { - ASSERT_TRUE(reader.OpenCurrentEntryInZip()); - actual_contents.insert(reader.current_entry_info()->file_path()); - ASSERT_TRUE(reader.AdvanceToNextEntry()); + 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_FALSE(reader.AdvanceToNextEntry()); // Shouldn't go further. - EXPECT_EQ(test_zip_contents_.size(), - static_cast(reader.num_entries())); - EXPECT_EQ(test_zip_contents_.size(), actual_contents.size()); - EXPECT_EQ(test_zip_contents_, actual_contents); + + 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, current_entry_info_RegularFile) { +TEST_F(ZipReaderTest, RegularFile) { ZipReader reader; ASSERT_TRUE(reader.Open(test_zip_file_)); base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - EXPECT_EQ(target_path, current_entry_info->file_path()); - EXPECT_EQ(13527, current_entry_info->original_size()); + const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path); + ASSERT_TRUE(entry); + + EXPECT_EQ(target_path, entry->path); + EXPECT_EQ(13527, entry->original_size); // The expected time stamp: 2009-05-29 06:22:20 base::Time::Exploded exploded = {}; // Zero-clear. - current_entry_info->last_modified().LocalExplode(&exploded); + entry->last_modified.UTCExplode(&exploded); EXPECT_EQ(2009, exploded.year); EXPECT_EQ(5, exploded.month); EXPECT_EQ(29, exploded.day_of_month); @@ -296,67 +300,106 @@ TEST_F(ZipReaderTest, current_entry_info_RegularFile) { EXPECT_EQ(20, exploded.second); EXPECT_EQ(0, exploded.millisecond); - EXPECT_FALSE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->is_directory()); + EXPECT_FALSE(entry->is_unsafe); + EXPECT_FALSE(entry->is_directory); } -TEST_F(ZipReaderTest, current_entry_info_DotDotFile) { +TEST_F(ZipReaderTest, DotDotFile) { ZipReader reader; - ASSERT_TRUE(reader.Open(evil_zip_file_)); + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil.zip"))); base::FilePath target_path(FILE_PATH_LITERAL( - "../levilevilevilevilevilevilevilevilevilevilevilevil")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - EXPECT_EQ(target_path, current_entry_info->file_path()); - - // This file is unsafe because of ".." in the file name. - EXPECT_TRUE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->is_directory()); + "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, current_entry_info_InvalidUTF8File) { +TEST_F(ZipReaderTest, InvalidUTF8File) { ZipReader reader; - ASSERT_TRUE(reader.Open(evil_via_invalid_utf8_zip_file_)); - // The evil file is the 2nd file in the zip file. - // We cannot locate by the file name ".\x80.\\evil.txt", - // as FilePath may internally convert the string. - ASSERT_TRUE(reader.AdvanceToNextEntry()); - ASSERT_TRUE(reader.OpenCurrentEntryInZip()); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil_via_invalid_utf8.zip"))); + base::FilePath target_path = base::FilePath::FromUTF8Unsafe(".�.�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); +} - // This file is unsafe because of invalid UTF-8 in the file name. - EXPECT_TRUE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->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 �. +TEST_F(ZipReaderTest, EncodingSjisAsUtf8) { + EXPECT_THAT( + GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip")), + ElementsAre( + base::FilePath::FromUTF8Unsafe("�V�����t�H���_/SJIS_835C_��.txt"), + base::FilePath::FromUTF8Unsafe( + "�V�����t�H���_/�V�����e�L�X�g �h�L�������g.txt"))); } -TEST_F(ZipReaderTest, current_entry_info_AbsoluteFile) { - ZipReader reader; - ASSERT_TRUE(reader.Open(evil_via_absolute_file_name_zip_file_)); - base::FilePath target_path(FILE_PATH_LITERAL("/evil.txt")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - EXPECT_EQ(target_path, current_entry_info->file_path()); +// 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_ƒ�.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_Г�.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"))); +} - // This file is unsafe because of the absolute file name. - EXPECT_TRUE(current_entry_info->is_unsafe()); - EXPECT_FALSE(current_entry_info->is_directory()); +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, current_entry_info_Directory) { +TEST_F(ZipReaderTest, Directory) { ZipReader reader; ASSERT_TRUE(reader.Open(test_zip_file_)); base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/")); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ZipReader::EntryInfo* current_entry_info = reader.current_entry_info(); - - EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("foo/bar/")), - current_entry_info->file_path()); + 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, current_entry_info->original_size()); + EXPECT_EQ(0, entry->original_size); // The expected time stamp: 2009-05-31 15:49:52 base::Time::Exploded exploded = {}; // Zero-clear. - current_entry_info->last_modified().LocalExplode(&exploded); + entry->last_modified.UTCExplode(&exploded); EXPECT_EQ(2009, exploded.year); EXPECT_EQ(5, exploded.month); EXPECT_EQ(31, exploded.day_of_month); @@ -365,22 +408,91 @@ TEST_F(ZipReaderTest, current_entry_info_Directory) { EXPECT_EQ(52, exploded.second); EXPECT_EQ(0, exploded.millisecond); - EXPECT_FALSE(current_entry_info->is_unsafe()); - EXPECT_TRUE(current_entry_info->is_directory()); + EXPECT_FALSE(entry->is_unsafe); + EXPECT_TRUE(entry->is_directory); } -TEST_F(ZipReaderTest, current_entry_info_EncryptedFile) { +TEST_F(ZipReaderTest, EncryptedFile_WrongPassword) { ZipReader reader; - base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); + ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("Different Encryptions.zip"))); + reader.SetPassword("wrong password"); - ASSERT_TRUE(reader.Open(encrypted_zip_file_)); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - EXPECT_TRUE(reader.current_entry_info()->is_encrypted()); - reader.Close(); + { + 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); + } - ASSERT_TRUE(reader.Open(test_zip_file_)); - ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - EXPECT_FALSE(reader.current_entry_info()->is_encrypted()); + for (const base::StringPiece 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 base::StringPiece 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 @@ -403,7 +515,7 @@ TEST_F(ZipReaderTest, OpenFromString) { "\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, base::size(kTestData)); + std::string data(kTestData, std::size(kTestData)); ZipReader reader; ASSERT_TRUE(reader.OpenFromString(data)); base::FilePath target_path(FILE_PATH_LITERAL("test.txt")); @@ -412,8 +524,8 @@ TEST_F(ZipReaderTest, OpenFromString) { test_dir_.AppendASCII("test.txt"))); std::string actual; - ASSERT_TRUE(base::ReadFileToString( - test_dir_.AppendASCII("test.txt"), &actual)); + ASSERT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("test.txt"), &actual)); EXPECT_EQ(std::string("This is a test.\n"), actual); } @@ -444,8 +556,8 @@ TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) { EXPECT_LE(1, listener.progress_calls()); std::string output; - ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), - &output)); + ASSERT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), &output)); const std::string md5 = base::MD5String(output); EXPECT_EQ(kQuuxExpectedMD5, md5); @@ -455,6 +567,103 @@ TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) { EXPECT_EQ(file_size, 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; @@ -489,7 +698,7 @@ TEST_F(ZipReaderTest, ExtractCurrentEntryToString) { // 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 = - test_data_dir_.AppendASCII("test_mismatch_size.zip"); + data_dir_.AppendASCII("test_mismatch_size.zip"); ZipReader reader; std::string contents; @@ -510,12 +719,12 @@ TEST_F(ZipReaderTest, ExtractCurrentEntryToString) { if (i > 0) { // Exact byte read limit: must pass. EXPECT_TRUE(reader.ExtractCurrentEntryToString(i, &contents)); - EXPECT_EQ(base::StringPiece("0123456", i).as_string(), contents); + EXPECT_EQ(std::string(base::StringPiece("0123456", i)), contents); } // More than necessary byte read limit: must pass. - EXPECT_TRUE(reader.ExtractCurrentEntryToString(16, &contents)); - EXPECT_EQ(base::StringPiece("0123456", i).as_string(), contents); + EXPECT_TRUE(reader.ExtractCurrentEntryToString(&contents)); + EXPECT_EQ(std::string(base::StringPiece("0123456", i)), contents); } reader.Close(); } @@ -525,7 +734,7 @@ TEST_F(ZipReaderTest, ExtractPartOfCurrentEntry) { // 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 = - test_data_dir_.AppendASCII("test_mismatch_size.zip"); + data_dir_.AppendASCII("test_mismatch_size.zip"); ZipReader reader; std::string contents; @@ -563,6 +772,37 @@ TEST_F(ZipReaderTest, ExtractPartOfCurrentEntry) { 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) { @@ -577,45 +817,40 @@ TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) { TEST_F(ZipReaderTest, ExtractCurrentEntryPrepareFailure) { testing::StrictMock mock_writer; - EXPECT_CALL(mock_writer, PrepareOutput()) - .WillOnce(Return(false)); + 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, std::numeric_limits::max())); + ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer)); } -// Test that when WriterDelegate::WriteBytes returns false, no other methods on -// the delegate are called and the extraction fails. +// 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 mock_writer; - EXPECT_CALL(mock_writer, PrepareOutput()) - .WillOnce(Return(true)); - EXPECT_CALL(mock_writer, WriteBytes(_, _)) - .WillOnce(Return(false)); + 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, std::numeric_limits::max())); + ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer)); } // Test that extraction succeeds when the writer delegate reports all is well. TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) { testing::StrictMock mock_writer; - EXPECT_CALL(mock_writer, PrepareOutput()) - .WillOnce(Return(true)); - EXPECT_CALL(mock_writer, WriteBytes(_, _)) - .WillRepeatedly(Return(true)); + 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")); @@ -623,50 +858,84 @@ TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) { ASSERT_TRUE(reader.Open(test_zip_file_)); ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); - ASSERT_TRUE(reader.ExtractCurrentEntry(&mock_writer, - std::numeric_limits::max())); + 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_TEMPORARY | - base::File::FLAG_DELETE_ON_CLOSE)); + 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()); } - // Writes data to the file, leaving the current position at the end of the - // write. - void PopulateFile() { - static const char kSomeData[] = "this sure is some data."; - static const size_t kSomeDataLen = sizeof(kSomeData) - 1; - ASSERT_NE(-1LL, file_.Write(0LL, kSomeData, kSomeDataLen)); - } - base::FilePath temp_file_path_; base::File file_; }; -TEST_F(FileWriterDelegateTest, WriteToStartAndTruncate) { - // Write stuff and advance. - PopulateFile(); +TEST_F(FileWriterDelegateTest, WriteToEnd) { + const std::string payload = "This is the actualy payload data.\n"; - // This should rewind, write, then truncate. - static const char kSomeData[] = "short"; - static const int kSomeDataLen = sizeof(kSomeData) - 1; { FileWriterDelegate writer(&file_); + EXPECT_EQ(0, writer.file_length()); ASSERT_TRUE(writer.PrepareOutput()); - ASSERT_TRUE(writer.WriteBytes(kSomeData, kSomeDataLen)); + ASSERT_TRUE(writer.WriteBytes(payload.data(), payload.size())); + EXPECT_EQ(payload.size(), writer.file_length()); } - ASSERT_EQ(kSomeDataLen, file_.GetLength()); - char buf[kSomeDataLen] = {}; - ASSERT_EQ(kSomeDataLen, file_.Read(0LL, buf, kSomeDataLen)); - ASSERT_EQ(std::string(kSomeData), std::string(buf, kSomeDataLen)); + + 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 diff --git a/deps/zlib/google/zip_unittest.cc b/deps/zlib/google/zip_unittest.cc index 7ea3c36c2a80f1..0006dbe1704a46 100644 --- a/deps/zlib/google/zip_unittest.cc +++ b/deps/zlib/google/zip_unittest.cc @@ -5,9 +5,11 @@ #include #include -#include -#include +#include +#include #include +#include +#include #include #include "base/bind.h" @@ -16,18 +18,43 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/macros.h" +#include "base/logging.h" #include "base/path_service.h" +#include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/test/bind.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/zlib/google/zip.h" +#include "third_party/zlib/google/zip_internal.h" #include "third_party/zlib/google/zip_reader.h" +// Convenience macro to create a file path from a string literal. +#define FP(path) base::FilePath(FILE_PATH_LITERAL(path)) + namespace { +using testing::UnorderedElementsAre; +using testing::UnorderedElementsAreArray; + +std::vector GetRelativePaths(const base::FilePath& dir, + base::FileEnumerator::FileType type) { + std::vector got_paths; + base::FileEnumerator files(dir, true, type); + for (base::FilePath path = files.Next(); !path.empty(); path = files.Next()) { + base::FilePath relative; + EXPECT_TRUE(dir.AppendRelativePath(path, &relative)); + got_paths.push_back(relative.NormalizePathSeparatorsTo('/').AsUTF8Unsafe()); + } + + EXPECT_EQ(base::File::FILE_OK, files.GetError()); + return got_paths; +} + bool CreateFile(const std::string& content, base::FilePath* file_path, base::File* file) { @@ -42,6 +69,46 @@ bool CreateFile(const std::string& content, return file->IsValid(); } +// A WriterDelegate that logs progress once per second. +class ProgressWriterDelegate : public zip::WriterDelegate { + public: + explicit ProgressWriterDelegate(int64_t expected_size) + : expected_size_(expected_size) { + CHECK_GT(expected_size_, 0); + } + + bool WriteBytes(const char* data, int num_bytes) override { + received_bytes_ += num_bytes; + LogProgressIfNecessary(); + return true; + } + + void SetTimeModified(const base::Time& time) override { LogProgress(); } + + int64_t received_bytes() const { return received_bytes_; } + + private: + void LogProgressIfNecessary() { + const base::TimeTicks now = base::TimeTicks::Now(); + if (next_progress_report_time_ > now) + return; + + next_progress_report_time_ = now + progress_period_; + LogProgress(); + } + + void LogProgress() const { + LOG(INFO) << "Unzipping... " << std::setw(3) + << (100 * received_bytes_ / expected_size_) << "%"; + } + + const base::TimeDelta progress_period_ = base::Seconds(1); + base::TimeTicks next_progress_report_time_ = + base::TimeTicks::Now() + progress_period_; + const uint64_t expected_size_; + int64_t received_bytes_ = 0; +}; + // A virtual file system containing: // /test // /test/foo.txt @@ -55,8 +122,8 @@ class VirtualFileSystem : public zip::FileAccessor { static constexpr char kBar2Content[] = "This is bar too."; VirtualFileSystem() { - base::FilePath test_dir(FILE_PATH_LITERAL("/test")); - base::FilePath foo_txt_path = test_dir.Append(FILE_PATH_LITERAL("foo.txt")); + base::FilePath test_dir; + base::FilePath foo_txt_path = test_dir.AppendASCII("foo.txt"); base::FilePath file_path; base::File file; @@ -64,62 +131,92 @@ class VirtualFileSystem : public zip::FileAccessor { DCHECK(success); files_[foo_txt_path] = std::move(file); - base::FilePath bar_dir = test_dir.Append(FILE_PATH_LITERAL("bar")); - base::FilePath bar1_txt_path = - bar_dir.Append(FILE_PATH_LITERAL("bar1.txt")); + base::FilePath bar_dir = test_dir.AppendASCII("bar"); + base::FilePath bar1_txt_path = bar_dir.AppendASCII("bar1.txt"); success = CreateFile(kBar1Content, &file_path, &file); DCHECK(success); files_[bar1_txt_path] = std::move(file); - base::FilePath bar2_txt_path = - bar_dir.Append(FILE_PATH_LITERAL("bar2.txt")); + base::FilePath bar2_txt_path = bar_dir.AppendASCII("bar2.txt"); success = CreateFile(kBar2Content, &file_path, &file); DCHECK(success); files_[bar2_txt_path] = std::move(file); - file_tree_[test_dir] = std::vector{ - DirectoryContentEntry(foo_txt_path, /*is_dir=*/false), - DirectoryContentEntry(bar_dir, /*is_dir=*/true)}; - file_tree_[bar_dir] = std::vector{ - DirectoryContentEntry(bar1_txt_path, /*is_dir=*/false), - DirectoryContentEntry(bar2_txt_path, /*is_dir=*/false)}; + file_tree_[base::FilePath()] = {{foo_txt_path}, {bar_dir}}; + file_tree_[bar_dir] = {{bar1_txt_path, bar2_txt_path}}; + file_tree_[foo_txt_path] = {}; + file_tree_[bar1_txt_path] = {}; + file_tree_[bar2_txt_path] = {}; } + + VirtualFileSystem(const VirtualFileSystem&) = delete; + VirtualFileSystem& operator=(const VirtualFileSystem&) = delete; + ~VirtualFileSystem() override = default; private: - std::vector OpenFilesForReading( - const std::vector& paths) override { - std::vector files; - for (const auto& path : paths) { - auto iter = files_.find(path); - files.push_back(iter == files_.end() ? base::File() - : std::move(iter->second)); + bool Open(const zip::Paths paths, + std::vector* const files) override { + DCHECK(files); + files->reserve(files->size() + paths.size()); + + for (const base::FilePath& path : paths) { + const auto it = files_.find(path); + if (it == files_.end()) { + files->emplace_back(); + } else { + EXPECT_TRUE(it->second.IsValid()); + files->push_back(std::move(it->second)); + } } - return files; - } - bool DirectoryExists(const base::FilePath& file) override { - return file_tree_.count(file) == 1 && files_.count(file) == 0; + return true; } - std::vector ListDirectoryContent( - const base::FilePath& dir) override { - auto iter = file_tree_.find(dir); - if (iter == file_tree_.end()) { - NOTREACHED(); - return std::vector(); + bool List(const base::FilePath& path, + std::vector* const files, + std::vector* const subdirs) override { + DCHECK(!path.IsAbsolute()); + DCHECK(files); + DCHECK(subdirs); + + const auto it = file_tree_.find(path); + if (it == file_tree_.end()) + return false; + + for (const base::FilePath& file : it->second.files) { + DCHECK(!file.empty()); + files->push_back(file); + } + + for (const base::FilePath& subdir : it->second.subdirs) { + DCHECK(!subdir.empty()); + subdirs->push_back(subdir); } - return iter->second; + + return true; } - base::Time GetLastModifiedTime(const base::FilePath& path) override { - return base::Time::FromDoubleT(172097977); // Some random date. + bool GetInfo(const base::FilePath& path, Info* const info) override { + DCHECK(!path.IsAbsolute()); + DCHECK(info); + + if (!file_tree_.count(path)) + return false; + + info->is_directory = !files_.count(path); + info->last_modified = + base::Time::FromDoubleT(172097977); // Some random date. + + return true; } - std::map> file_tree_; - std::map files_; + struct DirContents { + std::vector files, subdirs; + }; - DISALLOW_COPY_AND_ASSIGN(VirtualFileSystem); + std::unordered_map file_tree_; + std::unordered_map files_; }; // static @@ -130,10 +227,7 @@ constexpr char VirtualFileSystem::kBar2Content[]; // Make the test a PlatformTest to setup autorelease pools properly on Mac. class ZipTest : public PlatformTest { protected: - enum ValidYearType { - VALID_YEAR, - INVALID_YEAR - }; + enum ValidYearType { VALID_YEAR, INVALID_YEAR }; virtual void SetUp() { PlatformTest::SetUp(); @@ -142,94 +236,77 @@ class ZipTest : public PlatformTest { test_dir_ = temp_dir_.GetPath(); base::FilePath zip_path(test_dir_); - zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("foo.txt"))); - zip_path = zip_path.Append(FILE_PATH_LITERAL("foo")); + zip_contents_.insert(zip_path.AppendASCII("foo.txt")); + zip_path = zip_path.AppendASCII("foo"); zip_contents_.insert(zip_path); - zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("bar.txt"))); - zip_path = zip_path.Append(FILE_PATH_LITERAL("bar")); + zip_contents_.insert(zip_path.AppendASCII("bar.txt")); + zip_path = zip_path.AppendASCII("bar"); zip_contents_.insert(zip_path); - zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("baz.txt"))); - zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL("quux.txt"))); - zip_contents_.insert(zip_path.Append(FILE_PATH_LITERAL(".hidden"))); + zip_contents_.insert(zip_path.AppendASCII("baz.txt")); + zip_contents_.insert(zip_path.AppendASCII("quux.txt")); + zip_contents_.insert(zip_path.AppendASCII(".hidden")); // Include a subset of files in |zip_file_list_| to test ZipFiles(). - zip_file_list_.push_back(base::FilePath(FILE_PATH_LITERAL("foo.txt"))); - zip_file_list_.push_back( - base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt"))); - zip_file_list_.push_back( - base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden"))); + zip_file_list_.push_back(FP("foo.txt")); + zip_file_list_.push_back(FP("foo/bar/quux.txt")); + zip_file_list_.push_back(FP("foo/bar/.hidden")); } - virtual void TearDown() { - PlatformTest::TearDown(); - } + virtual void TearDown() { PlatformTest::TearDown(); } - bool GetTestDataDirectory(base::FilePath* path) { - bool success = base::PathService::Get(base::DIR_SOURCE_ROOT, path); + static base::FilePath GetDataDirectory() { + base::FilePath path; + bool success = base::PathService::Get(base::DIR_SOURCE_ROOT, &path); EXPECT_TRUE(success); - if (!success) - return false; - *path = path->AppendASCII("third_party"); - *path = path->AppendASCII("zlib"); - *path = path->AppendASCII("google"); - *path = path->AppendASCII("test"); - *path = path->AppendASCII("data"); - return true; + return std::move(path) + .AppendASCII("third_party") + .AppendASCII("zlib") + .AppendASCII("google") + .AppendASCII("test") + .AppendASCII("data"); } void TestUnzipFile(const base::FilePath::StringType& filename, bool expect_hidden_files) { - base::FilePath test_dir; - ASSERT_TRUE(GetTestDataDirectory(&test_dir)); - TestUnzipFile(test_dir.Append(filename), expect_hidden_files); + TestUnzipFile(GetDataDirectory().Append(filename), expect_hidden_files); } void TestUnzipFile(const base::FilePath& path, bool expect_hidden_files) { - ASSERT_TRUE(base::PathExists(path)) << "no file " << path.value(); + ASSERT_TRUE(base::PathExists(path)) << "no file " << path; ASSERT_TRUE(zip::Unzip(path, test_dir_)); - base::FilePath original_dir; - ASSERT_TRUE(GetTestDataDirectory(&original_dir)); - original_dir = original_dir.AppendASCII("test"); + base::FilePath original_dir = GetDataDirectory().AppendASCII("test"); - base::FileEnumerator files(test_dir_, true, + base::FileEnumerator files( + test_dir_, true, base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); - base::FilePath unzipped_entry_path = files.Next(); + size_t count = 0; - while (!unzipped_entry_path.value().empty()) { + for (base::FilePath unzipped_entry_path = files.Next(); + !unzipped_entry_path.empty(); unzipped_entry_path = files.Next()) { EXPECT_EQ(zip_contents_.count(unzipped_entry_path), 1U) - << "Couldn't find " << unzipped_entry_path.value(); + << "Couldn't find " << unzipped_entry_path; count++; if (base::PathExists(unzipped_entry_path) && !base::DirectoryExists(unzipped_entry_path)) { // It's a file, check its contents are what we zipped. - // TODO(774156): figure out why the commented out EXPECT_TRUE below - // fails on the build bots (but not on the try-bots). base::FilePath relative_path; - bool append_relative_path_success = - test_dir_.AppendRelativePath(unzipped_entry_path, &relative_path); - if (!append_relative_path_success) { - LOG(ERROR) << "Append relative path failed, params: " - << test_dir_.value() << " and " - << unzipped_entry_path.value(); - } + ASSERT_TRUE( + test_dir_.AppendRelativePath(unzipped_entry_path, &relative_path)) + << "Cannot append relative path failed, params: '" << test_dir_ + << "' and '" << unzipped_entry_path << "'"; base::FilePath original_path = original_dir.Append(relative_path); - LOG(ERROR) << "Comparing original " << original_path.value() - << " and unzipped file " << unzipped_entry_path.value() - << " result: " - << base::ContentsEqual(original_path, unzipped_entry_path); - // EXPECT_TRUE(base::ContentsEqual(original_path, unzipped_entry_path)) - // << "Contents differ between original " << original_path.value() - // << " and unzipped file " << unzipped_entry_path.value(); + EXPECT_TRUE(base::ContentsEqual(original_path, unzipped_entry_path)) + << "Original file '" << original_path << "' and unzipped file '" + << unzipped_entry_path << "' have different contents"; } - unzipped_entry_path = files.Next(); } + EXPECT_EQ(base::File::FILE_OK, files.GetError()); size_t expected_count = 0; - for (std::set::iterator iter = zip_contents_.begin(); - iter != zip_contents_.end(); ++iter) { - if (expect_hidden_files || iter->BaseName().value()[0] != '.') + for (const base::FilePath& path : zip_contents_) { + if (expect_hidden_files || path.BaseName().value()[0] != '.') ++expected_count; } @@ -265,11 +342,11 @@ class ZipTest : public PlatformTest { // supports, which is 2 seconds. Note that between this call to Time::Now() // and zip::Zip() the clock can advance a bit, hence the use of EXPECT_GE. base::Time::Exploded now_parts; - base::Time::Now().LocalExplode(&now_parts); + base::Time::Now().UTCExplode(&now_parts); now_parts.second = now_parts.second & ~1; now_parts.millisecond = 0; base::Time now_time; - EXPECT_TRUE(base::Time::FromLocalExploded(now_parts, &now_time)); + EXPECT_TRUE(base::Time::FromUTCExploded(now_parts, &now_time)); EXPECT_EQ(1, base::WriteFile(src_file, "1", 1)); EXPECT_TRUE(base::TouchFile(src_file, base::Time::Now(), test_mtime)); @@ -295,12 +372,23 @@ class ZipTest : public PlatformTest { base::ScopedTempDir temp_dir_; // Hard-coded contents of a known zip file. - std::set zip_contents_; + std::unordered_set zip_contents_; // Hard-coded list of relative paths for a zip file created with ZipFiles. std::vector zip_file_list_; }; +TEST_F(ZipTest, UnzipNoSuchFile) { + EXPECT_FALSE(zip::Unzip(GetDataDirectory().AppendASCII("No Such File.zip"), + test_dir_)); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre()); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); +} + TEST_F(ZipTest, Unzip) { TestUnzipFile(FILE_PATH_LITERAL("test.zip"), true); } @@ -310,108 +398,598 @@ TEST_F(ZipTest, UnzipUncompressed) { } TEST_F(ZipTest, UnzipEvil) { - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - path = path.AppendASCII("evil.zip"); + base::FilePath path = GetDataDirectory().AppendASCII("evil.zip"); // Unzip the zip file into a sub directory of test_dir_ so evil.zip // won't create a persistent file outside test_dir_ in case of a // failure. base::FilePath output_dir = test_dir_.AppendASCII("out"); - ASSERT_FALSE(zip::Unzip(path, output_dir)); - base::FilePath evil_file = output_dir; - evil_file = evil_file.AppendASCII( - "../levilevilevilevilevilevilevilevilevilevilevilevil"); - ASSERT_FALSE(base::PathExists(evil_file)); + EXPECT_TRUE(zip::Unzip(path, output_dir)); + EXPECT_TRUE(base::PathExists(output_dir.AppendASCII( + "UP/levilevilevilevilevilevilevilevilevilevilevilevil"))); } TEST_F(ZipTest, UnzipEvil2) { - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - // The zip file contains an evil file with invalid UTF-8 in its file - // name. - path = path.AppendASCII("evil_via_invalid_utf8.zip"); + // The ZIP file contains a file with invalid UTF-8 in its file name. + base::FilePath path = + GetDataDirectory().AppendASCII("evil_via_invalid_utf8.zip"); // See the comment at UnzipEvil() for why we do this. base::FilePath output_dir = test_dir_.AppendASCII("out"); - // This should fail as it contains an evil file. - ASSERT_FALSE(zip::Unzip(path, output_dir)); - base::FilePath evil_file = output_dir; - evil_file = evil_file.AppendASCII("../evil.txt"); - ASSERT_FALSE(base::PathExists(evil_file)); + ASSERT_TRUE(zip::Unzip(path, output_dir)); + ASSERT_TRUE(base::PathExists( + output_dir.Append(base::FilePath::FromUTF8Unsafe(".�.�evil.txt")))); + ASSERT_FALSE(base::PathExists(output_dir.AppendASCII("../evil.txt"))); } TEST_F(ZipTest, UnzipWithFilter) { auto filter = base::BindRepeating([](const base::FilePath& path) { return path.BaseName().MaybeAsASCII() == "foo.txt"; }); - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - ASSERT_TRUE(zip::UnzipWithFilterCallback(path.AppendASCII("test.zip"), - test_dir_, filter, false)); - // Only foo.txt should have been extracted. The following paths should not - // be extracted: - // foo/ - // foo/bar.txt - // foo/bar/ - // foo/bar/.hidden - // foo/bar/baz.txt - // foo/bar/quux.txt - ASSERT_TRUE(base::PathExists(test_dir_.AppendASCII("foo.txt"))); - base::FileEnumerator extractedFiles( - test_dir_, - false, // Do not enumerate recursively - the file must be in the root. - base::FileEnumerator::FileType::FILES); - int extracted_count = 0; - while (!extractedFiles.Next().empty()) - ++extracted_count; - ASSERT_EQ(1, extracted_count); - - base::FileEnumerator extractedDirs( - test_dir_, - false, // Do not enumerate recursively - we require zero directories. - base::FileEnumerator::FileType::DIRECTORIES); - extracted_count = 0; - while (!extractedDirs.Next().empty()) - ++extracted_count; - ASSERT_EQ(0, extracted_count); + ASSERT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("test.zip"), test_dir_, + {.filter = std::move(filter)})); + // Only foo.txt should have been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("foo.txt")); + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); +} + +TEST_F(ZipTest, UnzipEncryptedWithRightPassword) { + // TODO(crbug.com/1296838) Also check the AES-encrypted files. + auto filter = base::BindRepeating([](const base::FilePath& path) { + return !base::StartsWith(path.MaybeAsASCII(), "Encrypted AES"); + }); + + ASSERT_TRUE(zip::Unzip( + GetDataDirectory().AppendASCII("Different Encryptions.zip"), test_dir_, + {.filter = std::move(filter), .password = "password"})); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + ASSERT_TRUE(base::ReadFileToString( + test_dir_.AppendASCII("Encrypted ZipCrypto.txt"), &contents)); + EXPECT_EQ("This is encrypted with ZipCrypto.\n", contents); +} + +TEST_F(ZipTest, UnzipEncryptedWithWrongPassword) { + // TODO(crbug.com/1296838) Also check the AES-encrypted files. + auto filter = base::BindRepeating([](const base::FilePath& path) { + return !base::StartsWith(path.MaybeAsASCII(), "Encrypted AES"); + }); + + ASSERT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Different Encryptions.zip"), test_dir_, + {.filter = std::move(filter), .password = "wrong"})); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); +} + +TEST_F(ZipTest, UnzipEncryptedWithNoPassword) { + // TODO(crbug.com/1296838) Also check the AES-encrypted files. + auto filter = base::BindRepeating([](const base::FilePath& path) { + return !base::StartsWith(path.MaybeAsASCII(), "Encrypted AES"); + }); + + ASSERT_FALSE( + zip::Unzip(GetDataDirectory().AppendASCII("Different Encryptions.zip"), + test_dir_, {.filter = std::move(filter)})); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); +} + +TEST_F(ZipTest, UnzipEncryptedContinueOnError) { + EXPECT_TRUE( + zip::Unzip(GetDataDirectory().AppendASCII("Different Encryptions.zip"), + test_dir_, {.continue_on_error = true})); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"), + &contents)); + EXPECT_EQ("This is not encrypted.\n", contents); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("ClearText.txt")); +} + +TEST_F(ZipTest, UnzipWrongCrc) { + ASSERT_FALSE( + zip::Unzip(GetDataDirectory().AppendASCII("Wrong CRC.zip"), test_dir_)); + + // No rubbish file should be left behind. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre()); +} + +TEST_F(ZipTest, UnzipRepeatedDirName) { + EXPECT_TRUE(zip::Unzip( + GetDataDirectory().AppendASCII("Repeated Dir Name.zip"), test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre()); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre("repeated")); +} + +TEST_F(ZipTest, UnzipRepeatedFileName) { + EXPECT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Repeated File Name.zip"), test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("repeated")); + + std::string contents; + EXPECT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("repeated"), &contents)); + EXPECT_EQ("First file", contents); +} + +TEST_F(ZipTest, UnzipCannotCreateEmptyDir) { + EXPECT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Empty Dir Same Name As File.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("repeated")); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); + + std::string contents; + EXPECT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("repeated"), &contents)); + EXPECT_EQ("First file", contents); +} + +TEST_F(ZipTest, UnzipCannotCreateParentDir) { + EXPECT_FALSE(zip::Unzip( + GetDataDirectory().AppendASCII("Parent Dir Same Name As File.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("repeated")); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAre()); + + std::string contents; + EXPECT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("repeated"), &contents)); + EXPECT_EQ("First file", contents); +} + +// TODO(crbug.com/1311140) Detect and rename reserved file names on Windows. +TEST_F(ZipTest, UnzipWindowsSpecialNames) { + EXPECT_TRUE( + zip::Unzip(GetDataDirectory().AppendASCII("Windows Special Names.zip"), + test_dir_, {.continue_on_error = true})); + + std::unordered_set want_paths = { + "First", + "Last", + "CLOCK$", + " NUL.txt", +#ifndef OS_WIN + "NUL", + "NUL ", + "NUL.", + "NUL .", + "NUL.txt", + "NUL.tar.gz", + "NUL..txt", + "NUL...txt", + "NUL .txt", + "NUL .txt", + "NUL ..txt", +#ifndef OS_MAC + "Nul.txt", +#endif + "nul.very long extension", + "a/NUL", + "CON", + "PRN", + "AUX", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +#endif + }; + + const std::vector got_paths = + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES); + + for (const std::string& path : got_paths) { + EXPECT_TRUE(want_paths.erase(path)) + << "Found unexpected file: " << std::quoted(path); + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII(path), &contents)); + EXPECT_EQ(base::StrCat({"This is: ", path}), contents); + } + + for (const std::string& path : want_paths) { + EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path); + } +} + +TEST_F(ZipTest, UnzipDifferentCases) { +#if defined(OS_WIN) || defined(OS_MAC) + // Only the first file (with mixed case) is extracted. + EXPECT_FALSE(zip::Unzip(GetDataDirectory().AppendASCII( + "Repeated File Name With Different Cases.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case")); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); +#else + // All the files are extracted. + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII( + "Repeated File Name With Different Cases.zip"), + test_dir_)); + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case", "case", "CASE")); + + std::string contents; + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("case"), &contents)); + EXPECT_EQ("Lower case 22", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("CASE"), &contents)); + EXPECT_EQ("Upper case 3", contents); +#endif +} + +TEST_F(ZipTest, UnzipDifferentCasesContinueOnError) { + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII( + "Repeated File Name With Different Cases.zip"), + test_dir_, {.continue_on_error = true})); + + std::string contents; + +#if defined(OS_WIN) || defined(OS_MAC) + // Only the first file (with mixed case) has been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case")); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); +#else + // All the files have been extracted. + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES), + UnorderedElementsAre("Case", "case", "CASE")); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents)); + EXPECT_EQ("Mixed case 111", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("case"), &contents)); + EXPECT_EQ("Lower case 22", contents); + + EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("CASE"), &contents)); + EXPECT_EQ("Upper case 3", contents); +#endif +} + +TEST_F(ZipTest, UnzipMixedPaths) { + EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("Mixed Paths.zip"), + test_dir_, {.continue_on_error = true})); + + std::unordered_set want_paths = { +#ifdef OS_WIN + "Dot", // + "Space→", // +#else + " ", // + "AUX", // Disappears on Windows + "COM1", // Disappears on Windows + "COM2", // Disappears on Windows + "COM3", // Disappears on Windows + "COM4", // Disappears on Windows + "COM5", // Disappears on Windows + "COM6", // Disappears on Windows + "COM7", // Disappears on Windows + "COM8", // Disappears on Windows + "COM9", // Disappears on Windows + "CON", // Disappears on Windows + "Dot .", // + "LPT1", // Disappears on Windows + "LPT2", // Disappears on Windows + "LPT3", // Disappears on Windows + "LPT4", // Disappears on Windows + "LPT5", // Disappears on Windows + "LPT6", // Disappears on Windows + "LPT7", // Disappears on Windows + "LPT8", // Disappears on Windows + "LPT9", // Disappears on Windows + "NUL ..txt", // Disappears on Windows + "NUL .txt", // Disappears on Windows + "NUL ", // Disappears on Windows + "NUL .", // Disappears on Windows + "NUL .txt", // Disappears on Windows + "NUL", // Disappears on Windows + "NUL.", // Disappears on Windows + "NUL...txt", // Disappears on Windows + "NUL..txt", // Disappears on Windows + "NUL.tar.gz", // Disappears on Windows + "NUL.txt", // Disappears on Windows + "PRN", // Disappears on Windows + "Space→ ", // + "c/NUL", // Disappears on Windows + "nul.very long extension", // Disappears on Windows +#ifndef OS_MAC + "CASE", // Conflicts with "Case" + "case", // Conflicts with "Case" +#endif +#endif + " NUL.txt", // + " ←Space", // + "$HOME", // + "%TMP", // + "-", // + "...Three", // + "..Two", // + ".One", // + "Ampersand &", // + "Angle ��", // + "At @", // + "Backslash1→�", // + "Backslash3→�←Backslash4", // + "Backspace �", // + "Backtick `", // + "Bell �", // + "CLOCK$", // + "Caret ^", // + "Carriage Return �", // + "Case", // + "Colon �", // + "Comma ,", // + "Curly {}", // + "C�", // + "C��", // + "C��Temp", // + "C��Temp�", // + "C��Temp�File", // + "Dash -", // + "Delete \x7F", // + "Dollar $", // + "Double quote �", // + "Equal =", // + "Escape �", // + "Euro €", // + "Exclamation !", // + "FileOrDir", // + "First", // + "Hash #", // + "Last", // + "Line Feed �", // + "Percent %", // + "Pipe �", // + "Plus +", // + "Question �", // + "Quote '", // + "ROOT/At The Top", // + "ROOT/UP/Over The Top", // + "ROOT/dev/null", // + "Round ()", // + "Semicolon ;", // + "Smile \U0001F642", // + "Square []", // + "Star �", // + "String Terminator \u009C", // + "Tab �", // + "Tilde ~", // + "UP/One Level Up", // + "UP/UP/Two Levels Up", // + "Underscore _", // + "a/DOT/b", // + "a/UP/b", // + "u/v/w/x/y/z", // + "~", // + "�←Backslash2", // + "��server�share�file", // + }; + + const std::vector got_paths = + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES); + + for (const std::string& path : got_paths) { + EXPECT_TRUE(want_paths.erase(path)) + << "Found unexpected file: " << std::quoted(path); + } + + for (const std::string& path : want_paths) { + EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path); + } + + EXPECT_THAT( + GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES), + UnorderedElementsAreArray({ + "Empty", + "ROOT", + "ROOT/Empty", + "ROOT/UP", + "ROOT/dev", + "UP", + "UP/UP", + "a", + "a/DOT", + "a/UP", + "c", + "u", + "u/v", + "u/v/w", + "u/v/w/x", + "u/v/w/x/y", + })); } TEST_F(ZipTest, UnzipWithDelegates) { - auto filter = - base::BindRepeating([](const base::FilePath& path) { return true; }); - auto dir_creator = base::BindRepeating( - [](const base::FilePath& extract_dir, const base::FilePath& entry_path) { - return base::CreateDirectory(extract_dir.Append(entry_path)); - }, - test_dir_); - auto writer = base::BindRepeating( - [](const base::FilePath& extract_dir, const base::FilePath& entry_path) - -> std::unique_ptr { + auto dir_creator = + base::BindLambdaForTesting([this](const base::FilePath& entry_path) { + return base::CreateDirectory(test_dir_.Append(entry_path)); + }); + auto writer = + base::BindLambdaForTesting([this](const base::FilePath& entry_path) + -> std::unique_ptr { return std::make_unique( - extract_dir.Append(entry_path)); - }, - test_dir_); - base::FilePath path; - ASSERT_TRUE(GetTestDataDirectory(&path)); - base::File file(path.AppendASCII("test.zip"), + test_dir_.Append(entry_path)); + }); + + base::File file(GetDataDirectory().AppendASCII("test.zip"), + base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ); + EXPECT_TRUE(zip::Unzip(file.GetPlatformFile(), writer, dir_creator)); + base::FilePath dir = test_dir_; + base::FilePath dir_foo = dir.AppendASCII("foo"); + base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar"); + EXPECT_TRUE(base::PathExists(dir.AppendASCII("foo.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo)); + EXPECT_TRUE(base::PathExists(dir_foo.AppendASCII("bar.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo_bar)); + EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII(".hidden"))); + EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt"))); + EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); +} + +TEST_F(ZipTest, UnzipOnlyDirectories) { + auto dir_creator = + base::BindLambdaForTesting([this](const base::FilePath& entry_path) { + return base::CreateDirectory(test_dir_.Append(entry_path)); + }); + + // Always return a null WriterDelegate. + auto writer = + base::BindLambdaForTesting([](const base::FilePath& entry_path) { + return std::unique_ptr(); + }); + + base::File file(GetDataDirectory().AppendASCII("test.zip"), base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ); - ASSERT_TRUE(zip::UnzipWithFilterAndWriters(file.GetPlatformFile(), writer, - dir_creator, filter, false)); + EXPECT_TRUE(zip::Unzip(file.GetPlatformFile(), writer, dir_creator, + {.continue_on_error = true})); base::FilePath dir = test_dir_; base::FilePath dir_foo = dir.AppendASCII("foo"); base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar"); - ASSERT_TRUE(base::PathExists(dir.AppendASCII("foo.txt"))); - ASSERT_TRUE(base::PathExists(dir_foo)); - ASSERT_TRUE(base::PathExists(dir_foo.AppendASCII("bar.txt"))); - ASSERT_TRUE(base::PathExists(dir_foo_bar)); - ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII(".hidden"))); - ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt"))); - ASSERT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); + EXPECT_FALSE(base::PathExists(dir.AppendASCII("foo.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo)); + EXPECT_FALSE(base::PathExists(dir_foo.AppendASCII("bar.txt"))); + EXPECT_TRUE(base::DirectoryExists(dir_foo_bar)); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII(".hidden"))); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt"))); + EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt"))); +} + +// Tests that a ZIP archive containing SJIS-encoded file names can be correctly +// extracted if the encoding is specified. +TEST_F(ZipTest, UnzipSjis) { + ASSERT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("SJIS Bug 846195.zip"), + test_dir_, {.encoding = "Shift_JIS"})); + + const base::FilePath dir = + test_dir_.Append(base::FilePath::FromUTF8Unsafe("新しいフォルダ")); + EXPECT_TRUE(base::DirectoryExists(dir)); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString( + dir.Append(base::FilePath::FromUTF8Unsafe("SJIS_835C_ソ.txt")), + &contents)); + EXPECT_EQ( + "This file's name contains 0x5c (backslash) as the 2nd byte of Japanese " + "characater \"\x83\x5c\" when encoded in Shift JIS.", + contents); + + ASSERT_TRUE(base::ReadFileToString(dir.Append(base::FilePath::FromUTF8Unsafe( + "新しいテキスト ドキュメント.txt")), + &contents)); + EXPECT_EQ("This file name is coded in Shift JIS in the archive.", contents); +} + +// Tests that a ZIP archive containing SJIS-encoded file names can be extracted +// even if the encoding is not specified. In this case, file names are +// interpreted as UTF-8, which leads to garbled names where invalid UTF-8 +// sequences are replaced with the character �. Nevertheless, the files are +// safely extracted and readable. +TEST_F(ZipTest, UnzipSjisAsUtf8) { + ASSERT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("SJIS Bug 846195.zip"), + test_dir_)); + + EXPECT_FALSE(base::DirectoryExists( + test_dir_.Append(base::FilePath::FromUTF8Unsafe("新しいフォルダ")))); + + const base::FilePath dir = + test_dir_.Append(base::FilePath::FromUTF8Unsafe("�V�����t�H���_")); + EXPECT_TRUE(base::DirectoryExists(dir)); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString( + dir.Append(base::FilePath::FromUTF8Unsafe("SJIS_835C_��.txt")), + &contents)); + EXPECT_EQ( + "This file's name contains 0x5c (backslash) as the 2nd byte of Japanese " + "characater \"\x83\x5c\" when encoded in Shift JIS.", + contents); + + ASSERT_TRUE(base::ReadFileToString(dir.Append(base::FilePath::FromUTF8Unsafe( + "�V�����e�L�X�g �h�L�������g.txt")), + &contents)); + EXPECT_EQ("This file name is coded in Shift JIS in the archive.", contents); } TEST_F(ZipTest, Zip) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -422,9 +1000,7 @@ TEST_F(ZipTest, Zip) { } TEST_F(ZipTest, ZipIgnoreHidden) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -435,9 +1011,7 @@ TEST_F(ZipTest, ZipIgnoreHidden) { } TEST_F(ZipTest, ZipNonASCIIDir) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -473,11 +1047,9 @@ TEST_F(ZipTest, ZipTimeStamp) { TestTimeStamp("02 Jan 2038 23:59:58", VALID_YEAR); } -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) TEST_F(ZipTest, ZipFiles) { - base::FilePath src_dir; - ASSERT_TRUE(GetTestDataDirectory(&src_dir)); - src_dir = src_dir.AppendASCII("test"); + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -486,33 +1058,28 @@ TEST_F(ZipTest, ZipFiles) { base::File zip_file(zip_name, base::File::FLAG_CREATE | base::File::FLAG_WRITE); ASSERT_TRUE(zip_file.IsValid()); - EXPECT_TRUE(zip::ZipFiles(src_dir, zip_file_list_, - zip_file.GetPlatformFile())); + EXPECT_TRUE( + zip::ZipFiles(src_dir, zip_file_list_, zip_file.GetPlatformFile())); zip_file.Close(); zip::ZipReader reader; EXPECT_TRUE(reader.Open(zip_name)); EXPECT_EQ(zip_file_list_.size(), static_cast(reader.num_entries())); for (size_t i = 0; i < zip_file_list_.size(); ++i) { - EXPECT_TRUE(reader.HasMore()); - EXPECT_TRUE(reader.OpenCurrentEntryInZip()); - const zip::ZipReader::EntryInfo* entry_info = reader.current_entry_info(); - EXPECT_EQ(entry_info->file_path(), zip_file_list_[i]); - reader.AdvanceToNextEntry(); + const zip::ZipReader::Entry* const entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(entry->path, zip_file_list_[i]); } } -#endif // defined(OS_POSIX) +#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) TEST_F(ZipTest, UnzipFilesWithIncorrectSize) { - base::FilePath test_data_folder; - ASSERT_TRUE(GetTestDataDirectory(&test_data_folder)); - // test_mismatch_size.zip contains files with names from 0.txt to 7.txt with // sizes from 0 to 7 bytes respectively, but the metadata in the zip file says // the uncompressed size is 3 bytes. The ZipReader and minizip code needs to // be clever enough to get all the data out. base::FilePath test_zip_file = - test_data_folder.AppendASCII("test_mismatch_size.zip"); + GetDataDirectory().AppendASCII("test_mismatch_size.zip"); base::ScopedTempDir scoped_temp_dir; ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); @@ -523,8 +1090,8 @@ TEST_F(ZipTest, UnzipFilesWithIncorrectSize) { for (int i = 0; i < 8; i++) { SCOPED_TRACE(base::StringPrintf("Processing %d.txt", i)); - base::FilePath file_path = temp_dir.AppendASCII( - base::StringPrintf("%d.txt", i)); + base::FilePath file_path = + temp_dir.AppendASCII(base::StringPrintf("%d.txt", i)); int64_t file_size = -1; EXPECT_TRUE(base::GetFileSize(file_path, &file_size)); EXPECT_EQ(static_cast(i), file_size); @@ -534,26 +1101,309 @@ TEST_F(ZipTest, UnzipFilesWithIncorrectSize) { TEST_F(ZipTest, ZipWithFileAccessor) { base::FilePath zip_file; ASSERT_TRUE(base::CreateTemporaryFile(&zip_file)); - zip::ZipParams params(base::FilePath(FILE_PATH_LITERAL("/test")), zip_file); - params.set_file_accessor(std::make_unique()); + VirtualFileSystem file_accessor; + const zip::ZipParams params{.file_accessor = &file_accessor, + .dest_file = zip_file}; ASSERT_TRUE(zip::Zip(params)); base::ScopedTempDir scoped_temp_dir; ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); const base::FilePath& temp_dir = scoped_temp_dir.GetPath(); ASSERT_TRUE(zip::Unzip(zip_file, temp_dir)); - base::FilePath bar_dir = temp_dir.Append(FILE_PATH_LITERAL("bar")); + base::FilePath bar_dir = temp_dir.AppendASCII("bar"); EXPECT_TRUE(base::DirectoryExists(bar_dir)); std::string file_content; - EXPECT_TRUE(base::ReadFileToString( - temp_dir.Append(FILE_PATH_LITERAL("foo.txt")), &file_content)); + EXPECT_TRUE( + base::ReadFileToString(temp_dir.AppendASCII("foo.txt"), &file_content)); EXPECT_EQ(VirtualFileSystem::kFooContent, file_content); - EXPECT_TRUE(base::ReadFileToString( - bar_dir.Append(FILE_PATH_LITERAL("bar1.txt")), &file_content)); + EXPECT_TRUE( + base::ReadFileToString(bar_dir.AppendASCII("bar1.txt"), &file_content)); EXPECT_EQ(VirtualFileSystem::kBar1Content, file_content); - EXPECT_TRUE(base::ReadFileToString( - bar_dir.Append(FILE_PATH_LITERAL("bar2.txt")), &file_content)); + EXPECT_TRUE( + base::ReadFileToString(bar_dir.AppendASCII("bar2.txt"), &file_content)); EXPECT_EQ(VirtualFileSystem::kBar2Content, file_content); } +// Tests progress reporting while zipping files. +TEST_F(ZipTest, ZipProgress) { + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath zip_file = temp_dir.GetPath().AppendASCII("out.zip"); + + int progress_count = 0; + zip::Progress last_progress; + + zip::ProgressCallback progress_callback = + base::BindLambdaForTesting([&](const zip::Progress& progress) { + progress_count++; + LOG(INFO) << "Progress #" << progress_count << ": " << progress; + + // Progress should only go forwards. + EXPECT_GE(progress.bytes, last_progress.bytes); + EXPECT_GE(progress.files, last_progress.files); + EXPECT_GE(progress.directories, last_progress.directories); + + last_progress = progress; + return true; + }); + + EXPECT_TRUE(zip::Zip({.src_dir = src_dir, + .dest_file = zip_file, + .progress_callback = std::move(progress_callback)})); + + EXPECT_EQ(progress_count, 14); + EXPECT_EQ(last_progress.bytes, 13546); + EXPECT_EQ(last_progress.files, 5); + EXPECT_EQ(last_progress.directories, 2); + + TestUnzipFile(zip_file, true); +} + +// Tests throttling of progress reporting while zipping files. +TEST_F(ZipTest, ZipProgressPeriod) { + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath zip_file = temp_dir.GetPath().AppendASCII("out.zip"); + + int progress_count = 0; + zip::Progress last_progress; + + zip::ProgressCallback progress_callback = + base::BindLambdaForTesting([&](const zip::Progress& progress) { + progress_count++; + LOG(INFO) << "Progress #" << progress_count << ": " << progress; + + // Progress should only go forwards. + EXPECT_GE(progress.bytes, last_progress.bytes); + EXPECT_GE(progress.files, last_progress.files); + EXPECT_GE(progress.directories, last_progress.directories); + + last_progress = progress; + return true; + }); + + EXPECT_TRUE(zip::Zip({.src_dir = src_dir, + .dest_file = zip_file, + .progress_callback = std::move(progress_callback), + .progress_period = base::Hours(1)})); + + // We expect only 2 progress reports: the first one, and the last one. + EXPECT_EQ(progress_count, 2); + EXPECT_EQ(last_progress.bytes, 13546); + EXPECT_EQ(last_progress.files, 5); + EXPECT_EQ(last_progress.directories, 2); + + TestUnzipFile(zip_file, true); +} + +// Tests cancellation while zipping files. +TEST_F(ZipTest, ZipCancel) { + base::FilePath src_dir = GetDataDirectory().AppendASCII("test"); + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath zip_file = temp_dir.GetPath().AppendASCII("out.zip"); + + // First: establish the number of possible interruption points. + int progress_count = 0; + + EXPECT_TRUE(zip::Zip({.src_dir = src_dir, + .dest_file = zip_file, + .progress_callback = base::BindLambdaForTesting( + [&progress_count](const zip::Progress&) { + progress_count++; + return true; + })})); + + EXPECT_EQ(progress_count, 14); + + // Second: exercise each and every interruption point. + for (int i = progress_count; i > 0; i--) { + int j = 0; + EXPECT_FALSE(zip::Zip({.src_dir = src_dir, + .dest_file = zip_file, + .progress_callback = base::BindLambdaForTesting( + [i, &j](const zip::Progress&) { + j++; + // Callback shouldn't be called again after + // having returned false once. + EXPECT_LE(j, i); + return j < i; + })})); + + EXPECT_EQ(j, i); + } +} + +// Tests zip::internal::GetCompressionMethod() +TEST_F(ZipTest, GetCompressionMethod) { + using zip::internal::GetCompressionMethod; + using zip::internal::kDeflated; + using zip::internal::kStored; + + EXPECT_EQ(GetCompressionMethod(FP("")), kDeflated); + EXPECT_EQ(GetCompressionMethod(FP("NoExtension")), kDeflated); + EXPECT_EQ(GetCompressionMethod(FP("Folder.zip").Append(FP("NoExtension"))), + kDeflated); + EXPECT_EQ(GetCompressionMethod(FP("Name.txt")), kDeflated); + EXPECT_EQ(GetCompressionMethod(FP("Name.zip")), kStored); + EXPECT_EQ(GetCompressionMethod(FP("Name....zip")), kStored); + EXPECT_EQ(GetCompressionMethod(FP("Name.zip")), kStored); + EXPECT_EQ(GetCompressionMethod(FP("NAME.ZIP")), kStored); + EXPECT_EQ(GetCompressionMethod(FP("Name.gz")), kStored); + EXPECT_EQ(GetCompressionMethod(FP("Name.tar.gz")), kStored); + EXPECT_EQ(GetCompressionMethod(FP("Name.tar")), kDeflated); + + // This one is controversial. + EXPECT_EQ(GetCompressionMethod(FP(".zip")), kStored); +} + +// Tests that files put inside a ZIP are effectively compressed. +TEST_F(ZipTest, Compressed) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + const base::FilePath src_dir = temp_dir.GetPath().AppendASCII("input"); + EXPECT_TRUE(base::CreateDirectory(src_dir)); + + // Create some dummy source files. + for (const base::StringPiece s : {"foo", "bar.txt", ".hidden"}) { + base::File f(src_dir.AppendASCII(s), + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + ASSERT_TRUE(f.SetLength(5000)); + } + + // Zip the source files. + const base::FilePath dest_file = temp_dir.GetPath().AppendASCII("dest.zip"); + EXPECT_TRUE(zip::Zip({.src_dir = src_dir, + .dest_file = dest_file, + .include_hidden_files = true})); + + // Since the source files compress well, the destination ZIP file should be + // smaller than the source files. + int64_t dest_file_size; + ASSERT_TRUE(base::GetFileSize(dest_file, &dest_file_size)); + EXPECT_GT(dest_file_size, 300); + EXPECT_LT(dest_file_size, 1000); +} + +// Tests that a ZIP put inside a ZIP is simply stored instead of being +// compressed. +TEST_F(ZipTest, NestedZip) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + const base::FilePath src_dir = temp_dir.GetPath().AppendASCII("input"); + EXPECT_TRUE(base::CreateDirectory(src_dir)); + + // Create a dummy ZIP file. This is not a valid ZIP file, but for the purpose + // of this test, it doesn't really matter. + const int64_t src_size = 5000; + + { + base::File f(src_dir.AppendASCII("src.zip"), + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + ASSERT_TRUE(f.SetLength(src_size)); + } + + // Zip the dummy ZIP file. + const base::FilePath dest_file = temp_dir.GetPath().AppendASCII("dest.zip"); + EXPECT_TRUE(zip::Zip({.src_dir = src_dir, .dest_file = dest_file})); + + // Since the dummy source (inner) ZIP file should simply be stored in the + // destination (outer) ZIP file, the destination file should be bigger than + // the source file, but not much bigger. + int64_t dest_file_size; + ASSERT_TRUE(base::GetFileSize(dest_file, &dest_file_size)); + EXPECT_GT(dest_file_size, src_size + 100); + EXPECT_LT(dest_file_size, src_size + 300); +} + +// Tests that there is no 2GB or 4GB limits. Tests that big files can be zipped +// (crbug.com/1207737) and that big ZIP files can be created +// (crbug.com/1221447). Tests that the big ZIP can be opened, that its entries +// are correctly enumerated (crbug.com/1298347), and that the big file can be +// extracted. +// +// Because this test is dealing with big files, it tends to take a lot of disk +// space and time (crbug.com/1299736). Therefore, it only gets run on a few bots +// (ChromeOS and Windows). +// +// This test is too slow with TSAN. +// OS Fuchsia does not seem to support large files. +// Some 32-bit Android waterfall and CQ try bots are running out of space when +// performing this test (android-asan, android-11-x86-rel, +// android-marshmallow-x86-rel-non-cq). +// Some Mac, Linux and Debug (dbg) bots tend to time out when performing this +// test (crbug.com/1299736, crbug.com/1300448). +#if defined(THREAD_SANITIZER) || BUILDFLAG(IS_FUCHSIA) || \ + BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_CHROMEOS_LACROS) || !defined(NDEBUG) +TEST_F(ZipTest, DISABLED_BigFile) { +#else +TEST_F(ZipTest, BigFile) { +#endif + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + const base::FilePath src_dir = temp_dir.GetPath().AppendASCII("input"); + EXPECT_TRUE(base::CreateDirectory(src_dir)); + + // Create a big dummy ZIP file. This is not a valid ZIP file, but for the + // purpose of this test, it doesn't really matter. + const int64_t src_size = 5'000'000'000; + + const base::FilePath src_file = src_dir.AppendASCII("src.zip"); + LOG(INFO) << "Creating big file " << src_file; + { + base::File f(src_file, base::File::FLAG_CREATE | base::File::FLAG_WRITE); + ASSERT_TRUE(f.SetLength(src_size)); + } + + // Zip the dummy ZIP file. + const base::FilePath dest_file = temp_dir.GetPath().AppendASCII("dest.zip"); + LOG(INFO) << "Zipping big file into " << dest_file; + zip::ProgressCallback progress_callback = + base::BindLambdaForTesting([&](const zip::Progress& progress) { + LOG(INFO) << "Zipping... " << std::setw(3) + << (100 * progress.bytes / src_size) << "%"; + return true; + }); + EXPECT_TRUE(zip::Zip({.src_dir = src_dir, + .dest_file = dest_file, + .progress_callback = std::move(progress_callback), + .progress_period = base::Seconds(1)})); + + // Since the dummy source (inner) ZIP file should simply be stored in the + // destination (outer) ZIP file, the destination file should be bigger than + // the source file, but not much bigger. + int64_t dest_file_size; + ASSERT_TRUE(base::GetFileSize(dest_file, &dest_file_size)); + EXPECT_GT(dest_file_size, src_size + 100); + EXPECT_LT(dest_file_size, src_size + 300); + + LOG(INFO) << "Reading big ZIP " << dest_file; + zip::ZipReader reader; + ASSERT_TRUE(reader.Open(dest_file)); + + const zip::ZipReader::Entry* const entry = reader.Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(FP("src.zip"), entry->path); + EXPECT_EQ(src_size, entry->original_size); + EXPECT_FALSE(entry->is_directory); + EXPECT_FALSE(entry->is_encrypted); + + ProgressWriterDelegate writer(src_size); + EXPECT_TRUE(reader.ExtractCurrentEntry(&writer, + std::numeric_limits::max())); + EXPECT_EQ(src_size, writer.received_bytes()); + + EXPECT_FALSE(reader.Next()); + EXPECT_TRUE(reader.ok()); +} + } // namespace diff --git a/deps/zlib/google/zip_writer.cc b/deps/zlib/google/zip_writer.cc index 1f2f073b30cb32..e3f677fe328082 100644 --- a/deps/zlib/google/zip_writer.cc +++ b/deps/zlib/google/zip_writer.cc @@ -4,200 +4,305 @@ #include "third_party/zlib/google/zip_writer.h" +#include + #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 { -namespace { +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; -// Numbers of pending entries that trigger writting them to the ZIP file. -constexpr size_t kMaxPendingEntriesCount = 50; + LOG(ERROR) << "Cancelling ZIP creation"; + return false; +} -bool AddFileContentToZip(zipFile zip_file, - base::File file, - const base::FilePath& file_path) { - int num_bytes; +bool ZipWriter::AddFileContent(const base::FilePath& path, base::File file) { char buf[zip::internal::kZipBufSize]; - do { - num_bytes = file.ReadAtCurrentPos(buf, zip::internal::kZipBufSize); - if (num_bytes > 0) { - if (zipWriteInFileInZip(zip_file, buf, num_bytes) != ZIP_OK) { - DLOG(ERROR) << "Could not write data to zip for path " - << file_path.value(); - return false; - } + 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; } - } while (num_bytes > 0); - return true; + 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 OpenNewFileEntry(zipFile zip_file, - const base::FilePath& path, - bool is_directory, - base::Time last_modified) { +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 - if (is_directory) + + Compression compression = kDeflated; + + if (is_directory) { str_path += "/"; + } else { + compression = GetCompressionMethod(path); + } - return zip::internal::ZipOpenNewFileInZip(zip_file, str_path, last_modified); + return zip::internal::ZipOpenNewFileInZip(zip_file_, str_path, last_modified, + compression); } -bool CloseNewFileEntry(zipFile zip_file) { - return zipCloseFileInZip(zip_file) == ZIP_OK; +bool ZipWriter::CloseNewFileEntry() { + return zipCloseFileInZip(zip_file_) == ZIP_OK; } -bool AddFileEntryToZip(zipFile zip_file, - const base::FilePath& path, - base::File file) { - base::File::Info file_info; - if (!file.GetInfo(&file_info)) +bool ZipWriter::AddFileEntry(const base::FilePath& path, base::File file) { + base::File::Info info; + if (!file.GetInfo(&info)) return false; - if (!OpenNewFileEntry(zip_file, path, /*is_directory=*/false, - file_info.last_modified)) + if (!OpenNewFileEntry(path, /*is_directory=*/false, info.last_modified)) return false; - bool success = AddFileContentToZip(zip_file, std::move(file), path); - if (!CloseNewFileEntry(zip_file)) + if (!AddFileContent(path, std::move(file))) return false; - return success; + progress_.files++; + return CloseNewFileEntry(); } -bool AddDirectoryEntryToZip(zipFile zip_file, - const base::FilePath& path, - base::Time last_modified) { - return OpenNewFileEntry(zip_file, path, /*is_directory=*/true, - last_modified) && - CloseNewFileEntry(zip_file); -} +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_; + } -} // namespace + if (!OpenNewFileEntry(path, /*is_directory=*/true, info.last_modified)) + return false; + + if (!CloseNewFileEntry()) + return false; -#if defined(OS_POSIX) + 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::CreateWithFd( int zip_file_fd, - const base::FilePath& root_dir, 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) << "Couldn't create ZIP file for FD " << zip_file_fd; + DLOG(ERROR) << "Cannot create ZIP file for FD " << zip_file_fd; return nullptr; } - return std::unique_ptr( - new ZipWriter(zip_file, root_dir, file_accessor)); + + return std::unique_ptr(new ZipWriter(zip_file, file_accessor)); } #endif // static std::unique_ptr ZipWriter::Create( const base::FilePath& zip_file_path, - const base::FilePath& root_dir, FileAccessor* file_accessor) { DCHECK(!zip_file_path.empty()); zipFile zip_file = internal::OpenForZipping(zip_file_path.AsUTF8Unsafe(), APPEND_STATUS_CREATE); + if (!zip_file) { - DLOG(ERROR) << "Couldn't create ZIP file at path " << zip_file_path; + PLOG(ERROR) << "Cannot create ZIP file " << Redact(zip_file_path); return nullptr; } - return std::unique_ptr( - new ZipWriter(zip_file, root_dir, file_accessor)); -} - -ZipWriter::ZipWriter(zipFile zip_file, - const base::FilePath& root_dir, - FileAccessor* file_accessor) - : zip_file_(zip_file), root_dir_(root_dir), file_accessor_(file_accessor) {} -ZipWriter::~ZipWriter() { - DCHECK(pending_entries_.empty()); + return std::unique_ptr(new ZipWriter(zip_file, file_accessor)); } -bool ZipWriter::WriteEntries(const std::vector& paths) { - return AddEntries(paths) && Close(); -} +ZipWriter::ZipWriter(zipFile zip_file, FileAccessor* file_accessor) + : zip_file_(zip_file), file_accessor_(file_accessor) {} -bool ZipWriter::AddEntries(const std::vector& paths) { - DCHECK(zip_file_); - pending_entries_.insert(pending_entries_.end(), paths.begin(), paths.end()); - return FlushEntriesIfNeeded(/*force=*/false); +ZipWriter::~ZipWriter() { + if (zip_file_) + zipClose(zip_file_, nullptr); } bool ZipWriter::Close() { - bool success = FlushEntriesIfNeeded(/*force=*/true) && - zipClose(zip_file_, nullptr) == ZIP_OK; + 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::FlushEntriesIfNeeded(bool force) { - if (pending_entries_.size() < kMaxPendingEntriesCount && !force) - return true; +bool ZipWriter::AddMixedEntries(Paths paths) { + // Pointers to directory paths in |paths|. + std::vector directories; - while (pending_entries_.size() >= kMaxPendingEntriesCount || - (force && !pending_entries_.empty())) { - size_t entry_count = - std::min(pending_entries_.size(), kMaxPendingEntriesCount); - std::vector relative_paths; - std::vector absolute_paths; - relative_paths.insert(relative_paths.begin(), pending_entries_.begin(), - pending_entries_.begin() + entry_count); - for (auto iter = pending_entries_.begin(); - iter != pending_entries_.begin() + entry_count; ++iter) { - // The FileAccessor requires absolute paths. - absolute_paths.push_back(root_dir_.Append(*iter)); - } - pending_entries_.erase(pending_entries_.begin(), - pending_entries_.begin() + entry_count); - - // 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 entry. Open the - // files instead, invalid files are returned for directories. - std::vector files = - file_accessor_->OpenFilesForReading(absolute_paths); - DCHECK_EQ(files.size(), relative_paths.size()); - for (size_t i = 0; i < files.size(); i++) { + // Declared outside loop to reuse internal buffer. + std::vector 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(paths.size(), 50); + const Paths relative_paths = paths.subspan(0, n); + paths = paths.subspan(n, 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]; - const base::FilePath& absolute_path = absolute_paths[i]; - base::File file = std::move(files[i]); + DCHECK(!relative_path.empty()); + base::File& file = files[i]; + if (file.IsValid()) { - if (!AddFileEntryToZip(zip_file_, relative_path, std::move(file))) { - LOG(ERROR) << "Failed to write file " << relative_path.value() - << " to ZIP file."; + // It's a file. + if (!AddFileEntry(relative_path, std::move(file))) return false; - } } else { - // Missing file or directory case. - base::Time last_modified = - file_accessor_->GetLastModifiedTime(absolute_path); - if (last_modified.is_null()) { - LOG(ERROR) << "Failed to write entry " << relative_path.value() - << " to ZIP file."; - return false; - } - DCHECK(file_accessor_->DirectoryExists(absolute_path)); - if (!AddDirectoryEntryToZip(zip_file_, relative_path, last_modified)) { - LOG(ERROR) << "Failed to write directory " << relative_path.value() - << " to ZIP file."; - return false; - } + // 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 files; + + while (!paths.empty()) { + // Work with chunks of 50 paths at most. + const size_t n = std::min(paths.size(), 50); + const Paths relative_paths = paths.subspan(0, n); + paths = paths.subspan(n, paths.size() - 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 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* 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 diff --git a/deps/zlib/google/zip_writer.h b/deps/zlib/google/zip_writer.h index bd2a727b94d408..aa3c965d911599 100644 --- a/deps/zlib/google/zip_writer.h +++ b/deps/zlib/google/zip_writer.h @@ -9,6 +9,7 @@ #include #include "base/files/file_path.h" +#include "base/time/time.h" #include "build/build_config.h" #include "third_party/zlib/google/zip.h" @@ -28,64 +29,126 @@ namespace internal { // 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|/|zip_file| -// and which entries (specifies with AddEntries) are relative to |root_dir|. +// 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) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) static std::unique_ptr CreateWithFd(int zip_file_fd, - const base::FilePath& root_dir, FileAccessor* file_accessor); #endif + static std::unique_ptr Create(const base::FilePath& zip_file, - const base::FilePath& root_dir, FileAccessor* file_accessor); + + ZipWriter(const ZipWriter&) = delete; + ZipWriter& operator=(const ZipWriter&) = delete; + ~ZipWriter(); - // Writes the files at |paths| to the ZIP file and closes this Zip file. - // Note that the the FilePaths must be relative to |root_dir| specified in the - // Create method. - // Returns true if all entries were written successfuly. - bool WriteEntries(const std::vector& paths); + // 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: - ZipWriter(zipFile zip_file, - const base::FilePath& root_dir, - FileAccessor* file_accessor); + // Takes ownership of |zip_file|. + ZipWriter(zipFile zip_file, FileAccessor* file_accessor); - // Writes the pending entries to the ZIP file if there are at least - // |kMaxPendingEntriesCount| of them. If |force| is true, all pending entries - // are written regardless of how many there are. - // Returns false if writing an entry fails, true if no entry was written or - // there was no error writing entries. - bool FlushEntriesIfNeeded(bool force); + // Regularly called during processing to check whether zipping should continue + // or should be cancelled. + bool ShouldContinue(); - // Adds the files at |paths| to the ZIP file. These FilePaths must be relative - // to |root_dir| specified in the Create method. - bool AddEntries(const std::vector& paths); + // Adds file content to currently open file entry. + bool AddFileContent(const base::FilePath& path, base::File file); - // Closes the ZIP file. - // Returns true if successful, false otherwise (typically if an entry failed - // to be written). - bool Close(); + // 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); - // The entries that have been added but not yet written to the ZIP file. - std::vector pending_entries_; + // 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* paths); // The actual zip file. zipFile zip_file_; - // Path to the directory entry paths are relative to. - base::FilePath root_dir_; - // Abstraction over file access methods used to read files. - FileAccessor* file_accessor_; + 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; - DISALLOW_COPY_AND_ASSIGN(ZipWriter); + // Should ignore missing files and directories? + bool continue_on_error_ = false; }; } // namespace internal } // namespace zip -#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_WRITER_H_ \ No newline at end of file +#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_WRITER_H_ diff --git a/deps/zlib/gzguts.h b/deps/zlib/gzguts.h index 990a4d25149337..6378d468a258b1 100644 --- a/deps/zlib/gzguts.h +++ b/deps/zlib/gzguts.h @@ -39,7 +39,7 @@ # include #endif -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(_WIN32) # define WIDECHAR #endif diff --git a/deps/zlib/gzlib.c b/deps/zlib/gzlib.c index 4105e6aff92594..4838bf04745beb 100644 --- a/deps/zlib/gzlib.c +++ b/deps/zlib/gzlib.c @@ -5,7 +5,7 @@ #include "gzguts.h" -#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__) +#if defined(_WIN32) && !defined(__BORLANDC__) # define LSEEK _lseeki64 #else #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 @@ -397,7 +397,7 @@ z_off64_t ZEXPORT gzseek64(file, offset, whence) /* if within raw area while reading, just go there */ if (state->mode == GZ_READ && state->how == COPY && state->x.pos + offset >= 0) { - ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); + ret = LSEEK(state->fd, offset - (z_off64_t)state->x.have, SEEK_CUR); if (ret == -1) return -1; state->x.have = 0; diff --git a/deps/zlib/gzread.c b/deps/zlib/gzread.c index 832d3ef98c5948..85776cd25769dc 100644 --- a/deps/zlib/gzread.c +++ b/deps/zlib/gzread.c @@ -314,9 +314,9 @@ local z_size_t gz_read(state, buf, len) got = 0; do { /* set n to the maximum amount of len that fits in an unsigned int */ - n = -1; + n = (unsigned)-1; if (n > len) - n = len; + n = (unsigned)len; /* first just try copying data from the output buffer */ if (state->x.have) { @@ -397,7 +397,7 @@ int ZEXPORT gzread(file, buf, len) } /* read len or fewer bytes to buf */ - len = gz_read(state, buf, len); + len = (unsigned)gz_read(state, buf, len); /* check for an error */ if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) @@ -451,7 +451,6 @@ z_size_t ZEXPORT gzfread(buf, size, nitems, file) int ZEXPORT gzgetc(file) gzFile file; { - int ret; unsigned char buf[1]; gz_statep state; @@ -473,8 +472,7 @@ int ZEXPORT gzgetc(file) } /* nothing there -- try gz_read() */ - ret = gz_read(state, buf, 1); - return ret < 1 ? -1 : buf[0]; + return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; } int ZEXPORT gzgetc_(file) diff --git a/deps/zlib/gzwrite.c b/deps/zlib/gzwrite.c index c7b5651d70b994..3560193b8e3468 100644 --- a/deps/zlib/gzwrite.c +++ b/deps/zlib/gzwrite.c @@ -209,7 +209,7 @@ local z_size_t gz_write(state, buf, len) state->in); copy = state->size - have; if (copy > len) - copy = len; + copy = (unsigned)len; memcpy(state->in + have, buf, copy); state->strm.avail_in += copy; state->x.pos += copy; @@ -229,7 +229,7 @@ local z_size_t gz_write(state, buf, len) do { unsigned n = (unsigned)-1; if (n > len) - n = len; + n = (unsigned)len; state->strm.avail_in = n; state->x.pos += n; if (gz_comp(state, Z_NO_FLUSH) == -1) @@ -353,8 +353,7 @@ int ZEXPORT gzputs(file, str) gzFile file; const char *str; { - int ret; - z_size_t len; + z_size_t len, put; gz_statep state; /* get internal structure */ @@ -368,8 +367,12 @@ int ZEXPORT gzputs(file, str) /* write string */ len = strlen(str); - ret = gz_write(state, str, len); - return ret == 0 && len != 0 ? -1 : ret; + if ((int)len < 0 || (unsigned)len != len) { + gz_error(state, Z_STREAM_ERROR, "string length does not fit in int"); + return -1; + } + put = gz_write(state, str, len); + return put < len ? -1 : (int)len; } #if defined(STDC) || defined(Z_HAVE_STDARG_H) diff --git a/deps/zlib/inffast.c b/deps/zlib/inffast.c index 2797e8a03c5151..d89ad5ccdee0e2 100644 --- a/deps/zlib/inffast.c +++ b/deps/zlib/inffast.c @@ -74,7 +74,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ - code here; /* retrieved table entry */ + code const *here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ @@ -111,20 +111,20 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold += (unsigned long)(*in++) << bits; bits += 8; } - here = lcode[hold & lmask]; + here = lcode + (hold & lmask); dolen: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op == 0) { /* literal */ - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ? "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - *out++ = (unsigned char)(here.val); + "inflate: literal 0x%02x\n", here->val)); + *out++ = (unsigned char)(here->val); } else if (op & 16) { /* length base */ - len = (unsigned)(here.val); + len = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { @@ -142,14 +142,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold += (unsigned long)(*in++) << bits; bits += 8; } - here = dcode[hold & dmask]; + here = dcode + (hold & dmask); dodist: - op = (unsigned)(here.bits); + op = (unsigned)(here->bits); hold >>= op; bits -= op; - op = (unsigned)(here.op); + op = (unsigned)(here->op); if (op & 16) { /* distance base */ - dist = (unsigned)(here.val); + dist = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(*in++) << bits; @@ -268,7 +268,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level distance code */ - here = dcode[here.val + (hold & ((1U << op) - 1))]; + here = dcode + here->val + (hold & ((1U << op) - 1)); goto dodist; } else { @@ -278,7 +278,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } } else if ((op & 64) == 0) { /* 2nd level length code */ - here = lcode[here.val + (hold & ((1U << op) - 1))]; + here = lcode + here->val + (hold & ((1U << op) - 1)); goto dolen; } else if (op & 32) { /* end-of-block */ diff --git a/deps/zlib/patches/0003-uninitializedjump.patch b/deps/zlib/patches/0003-uninitializedjump.patch new file mode 100644 index 00000000000000..7aae3238a5011a --- /dev/null +++ b/deps/zlib/patches/0003-uninitializedjump.patch @@ -0,0 +1,15 @@ +diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c +index a39e62787862..c6053fd1c7ea 100644 +--- a/third_party/zlib/deflate.c ++++ b/third_party/zlib/deflate.c +@@ -318,6 +318,10 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + s->w_size + window_padding, + 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); ++ /* Avoid use of uninitialized value, see: ++ * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11360 ++ */ ++ zmemzero(s->prev, s->w_size * sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ diff --git a/deps/zlib/patches/0004-fix-uwp.patch b/deps/zlib/patches/0004-fix-uwp.patch new file mode 100644 index 00000000000000..23145a7ae5357f --- /dev/null +++ b/deps/zlib/patches/0004-fix-uwp.patch @@ -0,0 +1,22 @@ +diff --git a/third_party/zlib/contrib/minizip/iowin32.c b/third_party/zlib/contrib/minizip/iowin32.c +index 246ceb91a139..c6bc314b3c28 100644 +--- a/third_party/zlib/contrib/minizip/iowin32.c ++++ b/third_party/zlib/contrib/minizip/iowin32.c +@@ -31,14 +31,12 @@ + #define _WIN32_WINNT 0x601 + #endif + +-#if _WIN32_WINNT >= _WIN32_WINNT_WIN8 +-// see Include/shared/winapifamily.h in the Windows Kit +-#if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API))) +-#if WINAPI_FAMILY_ONE_PARTITION(WINAPI_FAMILY, WINAPI_PARTITION_APP) ++#if !defined(IOWIN32_USING_WINRT_API) ++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) ++// Windows Store or Universal Windows Platform + #define IOWIN32_USING_WINRT_API 1 + #endif + #endif +-#endif + + voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); + uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); diff --git a/deps/zlib/patches/0005-infcover-gtest.patch b/deps/zlib/patches/0005-infcover-gtest.patch new file mode 100644 index 00000000000000..f5443bd7d88e1c --- /dev/null +++ b/deps/zlib/patches/0005-infcover-gtest.patch @@ -0,0 +1,405 @@ +From 409594639f15d825202971db7a275023e05772ff Mon Sep 17 00:00:00 2001 +From: Adenilson Cavalcanti +Date: Tue, 28 Apr 2020 10:48:01 -0700 +Subject: [PATCH] Local Changes: - make C tests build as C++ code so we can + use gtest. - use gtest EXPECT_TRUE instead of C assert. - replace C + streams for C++ (portability issues). + +--- + test/infcover.c | 167 ++++++++++++++++++++++++++---------------------- + 1 file changed, 90 insertions(+), 77 deletions(-) + +diff --git a/test/infcover.c b/test/infcover.c +index 2be0164..a8c51c7 100644 +--- a/test/infcover.c ++++ b/test/infcover.c +@@ -4,11 +4,12 @@ + */ + + /* to use, do: ./configure --cover && make cover */ +- ++// clang-format off ++#include "infcover.h" + #include + #include + #include +-#include ++ + #include "zlib.h" + + /* get definition of internal structure so we can mess with it (see pull()), +@@ -17,8 +18,22 @@ + #include "inftrees.h" + #include "inflate.h" + ++/* XXX: use C++ streams instead of printf/fputs/etc due to portability ++ * as type sizes can vary between platforms. ++ */ ++#include + #define local static + ++/* XXX: hacking C assert and plugging into GTest. */ ++#include "gtest.h" ++#if defined(assert) ++#undef assert ++#define assert EXPECT_TRUE ++#endif ++ ++/* XXX: handle what is a reserved word in C++. */ ++#define try try_f ++ + /* -- memory tracking routines -- */ + + /* +@@ -72,7 +87,7 @@ local void *mem_alloc(void *mem, unsigned count, unsigned size) + { + void *ptr; + struct mem_item *item; +- struct mem_zone *zone = mem; ++ struct mem_zone *zone = static_cast(mem); + size_t len = count * (size_t)size; + + /* induced allocation failure */ +@@ -87,7 +102,7 @@ local void *mem_alloc(void *mem, unsigned count, unsigned size) + memset(ptr, 0xa5, len); + + /* create a new item for the list */ +- item = malloc(sizeof(struct mem_item)); ++ item = static_cast(malloc(sizeof(struct mem_item))); + if (item == NULL) { + free(ptr); + return NULL; +@@ -112,7 +127,7 @@ local void *mem_alloc(void *mem, unsigned count, unsigned size) + local void mem_free(void *mem, void *ptr) + { + struct mem_item *item, *next; +- struct mem_zone *zone = mem; ++ struct mem_zone *zone = static_cast(mem); + + /* if no zone, just do a free */ + if (zone == NULL) { +@@ -159,7 +174,7 @@ local void mem_setup(z_stream *strm) + { + struct mem_zone *zone; + +- zone = malloc(sizeof(struct mem_zone)); ++ zone = static_cast(malloc(sizeof(struct mem_zone))); + assert(zone != NULL); + zone->first = NULL; + zone->total = 0; +@@ -175,33 +190,33 @@ local void mem_setup(z_stream *strm) + /* set a limit on the total memory allocation, or 0 to remove the limit */ + local void mem_limit(z_stream *strm, size_t limit) + { +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast(strm->opaque); + + zone->limit = limit; + } + + /* show the current total requested allocations in bytes */ +-local void mem_used(z_stream *strm, char *prefix) ++local void mem_used(z_stream *strm, const char *prefix) + { +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast(strm->opaque); + +- fprintf(stderr, "%s: %lu allocated\n", prefix, zone->total); ++ std::cout << prefix << ": " << zone->total << " allocated" << std::endl; + } + + /* show the high water allocation in bytes */ +-local void mem_high(z_stream *strm, char *prefix) ++local void mem_high(z_stream *strm, const char *prefix) + { +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast(strm->opaque); + +- fprintf(stderr, "%s: %lu high water mark\n", prefix, zone->highwater); ++ std::cout << prefix << ": " << zone->highwater << " high water mark" << std::endl; + } + + /* release the memory allocation zone -- if there are any surprises, notify */ +-local void mem_done(z_stream *strm, char *prefix) ++local void mem_done(z_stream *strm, const char *prefix) + { + int count = 0; + struct mem_item *item, *next; +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast(strm->opaque); + + /* show high water mark */ + mem_high(strm, prefix); +@@ -218,13 +233,20 @@ local void mem_done(z_stream *strm, char *prefix) + + /* issue alerts about anything unexpected */ + if (count || zone->total) +- fprintf(stderr, "** %s: %lu bytes in %d blocks not freed\n", +- prefix, zone->total, count); ++ std::cout << "** " << prefix << ": " ++ << zone->total << " bytes in " ++ << count << " blocks not freed" ++ << std::endl; ++ + if (zone->notlifo) +- fprintf(stderr, "** %s: %d frees not LIFO\n", prefix, zone->notlifo); ++ std::cout << "** " << prefix << ": " ++ << zone->notlifo << " frees not LIFO" ++ << std::endl; ++ + if (zone->rogue) +- fprintf(stderr, "** %s: %d frees not recognized\n", +- prefix, zone->rogue); ++ std::cout << "** " << prefix << ": " ++ << zone->rogue << " frees not recognized" ++ << std::endl; + + /* free the zone and delete from the stream */ + free(zone); +@@ -247,7 +269,7 @@ local unsigned char *h2b(const char *hex, unsigned *len) + unsigned char *in, *re; + unsigned next, val; + +- in = malloc((strlen(hex) + 1) >> 1); ++ in = static_cast(malloc((strlen(hex) + 1) >> 1)); + if (in == NULL) + return NULL; + next = 0; +@@ -268,7 +290,7 @@ local unsigned char *h2b(const char *hex, unsigned *len) + } while (*hex++); /* go through the loop with the terminating null */ + if (len != NULL) + *len = next; +- re = realloc(in, next); ++ re = static_cast(realloc(in, next)); + return re == NULL ? in : re; + } + +@@ -281,7 +303,7 @@ local unsigned char *h2b(const char *hex, unsigned *len) + header information is collected with inflateGetHeader(). If a zlib stream + is looking for a dictionary, then an empty dictionary is provided. + inflate() is run until all of the input data is consumed. */ +-local void inf(char *hex, char *what, unsigned step, int win, unsigned len, ++local void inf(const char *hex, const char *what, unsigned step, int win, unsigned len, + int err) + { + int ret; +@@ -298,7 +320,7 @@ local void inf(char *hex, char *what, unsigned step, int win, unsigned len, + mem_done(&strm, what); + return; + } +- out = malloc(len); assert(out != NULL); ++ out = static_cast(malloc(len)); assert(out != NULL); + if (win == 47) { + head.extra = out; + head.extra_max = len; +@@ -347,7 +369,7 @@ local void inf(char *hex, char *what, unsigned step, int win, unsigned len, + } + + /* cover all of the lines in inflate.c up to inflate() */ +-local void cover_support(void) ++void cover_support(void) + { + int ret; + z_stream strm; +@@ -381,11 +403,11 @@ local void cover_support(void) + strm.next_in = Z_NULL; + ret = inflateInit(&strm); assert(ret == Z_OK); + ret = inflateEnd(&strm); assert(ret == Z_OK); +- fputs("inflate built-in memory routines\n", stderr); ++ std::cout << "inflate built-in memory routines" << std::endl;; + } + + /* cover all inflate() header and trailer cases and code after inflate() */ +-local void cover_wrap(void) ++void cover_wrap(void) + { + int ret; + z_stream strm, copy; +@@ -394,7 +416,7 @@ local void cover_wrap(void) + ret = inflate(Z_NULL, 0); assert(ret == Z_STREAM_ERROR); + ret = inflateEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); + ret = inflateCopy(Z_NULL, Z_NULL); assert(ret == Z_STREAM_ERROR); +- fputs("inflate bad parameters\n", stderr); ++ std::cout << "inflate bad parameters" << std::endl; + + inf("1f 8b 0 0", "bad gzip method", 0, 31, 0, Z_DATA_ERROR); + inf("1f 8b 8 80", "bad gzip flags", 0, 31, 0, Z_DATA_ERROR); +@@ -415,9 +437,9 @@ local void cover_wrap(void) + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, -8); + strm.avail_in = 2; +- strm.next_in = (void *)"\x63"; ++ strm.next_in = (Bytef *)"\x63"; + strm.avail_out = 1; +- strm.next_out = (void *)&ret; ++ strm.next_out = (Bytef *)&ret; + mem_limit(&strm, 1); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); +@@ -428,11 +450,11 @@ local void cover_wrap(void) + mem_limit(&strm, (sizeof(struct inflate_state) << 1) + 256); + ret = inflatePrime(&strm, 16, 0); assert(ret == Z_OK); + strm.avail_in = 2; +- strm.next_in = (void *)"\x80"; ++ strm.next_in = (Bytef *)"\x80"; + ret = inflateSync(&strm); assert(ret == Z_DATA_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_STREAM_ERROR); + strm.avail_in = 4; +- strm.next_in = (void *)"\0\0\xff\xff"; ++ strm.next_in = (Bytef *)"\0\0\xff\xff"; + ret = inflateSync(&strm); assert(ret == Z_OK); + (void)inflateSyncPoint(&strm); + ret = inflateCopy(©, &strm); assert(ret == Z_MEM_ERROR); +@@ -454,7 +476,7 @@ local unsigned pull(void *desc, unsigned char **buf) + next = 0; + return 0; /* no input (already provided at next_in) */ + } +- state = (void *)((z_stream *)desc)->state; ++ state = reinterpret_cast(((z_stream *)desc)->state); + if (state != Z_NULL) + state->mode = SYNC; /* force an otherwise impossible situation */ + return next < sizeof(dat) ? (*buf = dat + next++, 1) : 0; +@@ -467,7 +489,7 @@ local int push(void *desc, unsigned char *buf, unsigned len) + } + + /* cover inflateBack() up to common deflate data cases and after those */ +-local void cover_back(void) ++void cover_back(void) + { + int ret; + z_stream strm; +@@ -479,17 +501,17 @@ local void cover_back(void) + ret = inflateBack(Z_NULL, Z_NULL, Z_NULL, Z_NULL, Z_NULL); + assert(ret == Z_STREAM_ERROR); + ret = inflateBackEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); +- fputs("inflateBack bad parameters\n", stderr); ++ std::cout << "inflateBack bad parameters" << std::endl;; + + mem_setup(&strm); + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + strm.avail_in = 2; +- strm.next_in = (void *)"\x03"; ++ strm.next_in = (Bytef *)"\x03"; + ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); + assert(ret == Z_STREAM_END); + /* force output error */ + strm.avail_in = 3; +- strm.next_in = (void *)"\x63\x00"; ++ strm.next_in = (Bytef *)"\x63\x00"; + ret = inflateBack(&strm, pull, Z_NULL, push, &strm); + assert(ret == Z_BUF_ERROR); + /* force mode error by mucking with state */ +@@ -500,11 +522,11 @@ local void cover_back(void) + + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + ret = inflateBackEnd(&strm); assert(ret == Z_OK); +- fputs("inflateBack built-in memory routines\n", stderr); ++ std::cout << "inflateBack built-in memory routines" << std::endl;; + } + + /* do a raw inflate of data in hexadecimal with both inflate and inflateBack */ +-local int try(char *hex, char *id, int err) ++local int try(const char *hex, const char *id, int err) + { + int ret; + unsigned len, size; +@@ -518,11 +540,11 @@ local int try(char *hex, char *id, int err) + + /* allocate work areas */ + size = len << 3; +- out = malloc(size); ++ out = static_cast(malloc(size)); + assert(out != NULL); +- win = malloc(32768); ++ win = static_cast(malloc(32768)); + assert(win != NULL); +- prefix = malloc(strlen(id) + 6); ++ prefix = static_cast(malloc(strlen(id) + 6)); + assert(prefix != NULL); + + /* first with inflate */ +@@ -578,7 +600,7 @@ local int try(char *hex, char *id, int err) + } + + /* cover deflate data cases in both inflate() and inflateBack() */ +-local void cover_inflate(void) ++void cover_inflate(void) + { + try("0 0 0 0 0", "invalid stored block lengths", 1); + try("3 0", "fixed", 0); +@@ -613,32 +635,33 @@ local void cover_inflate(void) + inf("63 18 5 40 c 0", "window wrap", 3, -8, 300, Z_OK); + } + ++/* XXX(cavalcantii): fix linking error due inflate_table. */ + /* cover remaining lines in inftrees.c */ +-local void cover_trees(void) +-{ +- int ret; +- unsigned bits; +- unsigned short lens[16], work[16]; +- code *next, table[ENOUGH_DISTS]; +- +- /* we need to call inflate_table() directly in order to manifest not- +- enough errors, since zlib insures that enough is always enough */ +- for (bits = 0; bits < 15; bits++) +- lens[bits] = (unsigned short)(bits + 1); +- lens[15] = 15; +- next = table; +- bits = 15; +- ret = inflate_table(DISTS, lens, 16, &next, &bits, work); +- assert(ret == 1); +- next = table; +- bits = 1; +- ret = inflate_table(DISTS, lens, 16, &next, &bits, work); +- assert(ret == 1); +- fputs("inflate_table not enough errors\n", stderr); +-} ++/* void cover_trees(void) */ ++/* { */ ++/* int ret; */ ++/* unsigned bits; */ ++/* unsigned short lens[16], work[16]; */ ++/* code *next, table[ENOUGH_DISTS]; */ ++ ++/* /\* we need to call inflate_table() directly in order to manifest not- */ ++/* enough errors, since zlib insures that enough is always enough *\/ */ ++/* for (bits = 0; bits < 15; bits++) */ ++/* lens[bits] = (unsigned short)(bits + 1); */ ++/* lens[15] = 15; */ ++/* next = table; */ ++/* bits = 15; */ ++/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ ++/* assert(ret == 1); */ ++/* next = table; */ ++/* bits = 1; */ ++/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ ++/* assert(ret == 1); */ ++/* fputs("inflate_table not enough errors\n", stderr); */ ++/* } */ + + /* cover remaining inffast.c decoding and window copying */ +-local void cover_fast(void) ++void cover_fast(void) + { + inf("e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68" + " ff 7f 0f 0 0 0", "fast length extra bits", 0, -8, 258, Z_DATA_ERROR); +@@ -658,14 +681,4 @@ local void cover_fast(void) + Z_STREAM_END); + } + +-int main(void) +-{ +- fprintf(stderr, "%s\n", zlibVersion()); +- cover_support(); +- cover_wrap(); +- cover_back(); +- cover_inflate(); +- cover_trees(); +- cover_fast(); +- return 0; +-} ++// clang-format on +-- +2.21.1 (Apple Git-122.3) + diff --git a/deps/zlib/patches/0006-fix-check_match.patch b/deps/zlib/patches/0006-fix-check_match.patch new file mode 100644 index 00000000000000..b21c363b1ec1e7 --- /dev/null +++ b/deps/zlib/patches/0006-fix-check_match.patch @@ -0,0 +1,42 @@ +From 8304bdda5293ffd5b3efce8e4f54904b387029d6 Mon Sep 17 00:00:00 2001 +From: Hans Wennborg +Date: Wed, 23 Sep 2020 16:36:38 +0200 +Subject: [PATCH] Avoid crashing in check_match when prev_match == -1 + +prev_match can be set to -1 after sliding the window. In that case, the +window has slid past the first byte of the last match, which means it +cannot be compared in check_match. + +This would cause zlib to crash on some inputs to deflate when built +with ZLIB_DEBUG enabled. + +Check for this situation and avoid crashing by not trying to compare +the first byte. + +Bug: 1113142 +--- + third_party/zlib/deflate.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c +index cfdd2f46b230..d70732ec6fc2 100644 +--- a/third_party/zlib/deflate.c ++++ b/third_party/zlib/deflate.c +@@ -2060,7 +2060,13 @@ local block_state deflate_slow(s, flush) + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + +- check_match(s, s->strstart-1, s->prev_match, s->prev_length); ++ if (s->prev_match == -1) { ++ /* The window has slid one byte past the previous match, ++ * so the first byte cannot be compared. */ ++ check_match(s, s->strstart, s->prev_match+1, s->prev_length-1); ++ } else { ++ check_match(s, s->strstart-1, s->prev_match, s->prev_length); ++ } + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); +-- +2.28.0.681.g6f77f65b4e-goog + diff --git a/deps/zlib/patches/0007-zero-init-deflate-window.patch b/deps/zlib/patches/0007-zero-init-deflate-window.patch new file mode 100644 index 00000000000000..9dbbf53a8c6b6e --- /dev/null +++ b/deps/zlib/patches/0007-zero-init-deflate-window.patch @@ -0,0 +1,40 @@ +From 92537ee19784e0e545f06d89b7d89ab532a18cff Mon Sep 17 00:00:00 2001 +From: Hans Wennborg +Date: Tue, 3 Nov 2020 15:54:09 +0100 +Subject: [PATCH] [zlib] Zero-initialize the window used for deflation + +Otherwise MSan complains about use-of-uninitialized values in the +window. +This happens in both regular deflate's longest_match and deflate_rle. + +Before crrev.com/822755 we used to suppress those reports, but it seems +better to fix it properly. That will also allow us to catch other +potential issues with MSan in these functions. + +The instances of this that we've seen only reproduce with +fill_window_sse(), not with the regular fill_window() function. Since +the former doesn't exist in upstream zlib, I'm not planning to send this +patch upstream. + +Bug: 1137613, 1144420 +--- + third_party/zlib/deflate.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c +index 8bf93e524875..fc7ae45905ff 100644 +--- a/third_party/zlib/deflate.c ++++ b/third_party/zlib/deflate.c +@@ -321,6 +321,9 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + s->window = (Bytef *) ZALLOC(strm, + s->w_size + window_padding, + 2*sizeof(Byte)); ++ /* Avoid use of unitialized values in the window, see crbug.com/1137613 and ++ * crbug.com/1144420 */ ++ zmemzero(s->window, (s->w_size + window_padding) * (2 * sizeof(Byte))); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + /* Avoid use of uninitialized value, see: + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11360 +-- +2.29.1.341.ge80a0c044ae-goog + diff --git a/deps/zlib/patches/0008-minizip-zip-unzip-tools.patch b/deps/zlib/patches/0008-minizip-zip-unzip-tools.patch new file mode 100644 index 00000000000000..48ceb02d4c3781 --- /dev/null +++ b/deps/zlib/patches/0008-minizip-zip-unzip-tools.patch @@ -0,0 +1,98 @@ +From 0c7de17000659f4f79de878296892c46be0aff77 Mon Sep 17 00:00:00 2001 +From: Noel Gordon +Date: Wed, 26 May 2021 21:57:43 +1000 +Subject: [PATCH] Build minizip zip and unzip tools + +--- + third_party/zlib/contrib/minizip/miniunz.c | 13 ++++++------- + third_party/zlib/contrib/minizip/minizip.c | 7 +++---- + 2 files changed, 9 insertions(+), 11 deletions(-) + +diff --git a/third_party/zlib/contrib/minizip/miniunz.c b/third_party/zlib/contrib/minizip/miniunz.c +index 3d65401be5cd..08737f689a96 100644 +--- a/third_party/zlib/contrib/minizip/miniunz.c ++++ b/third_party/zlib/contrib/minizip/miniunz.c +@@ -12,7 +12,7 @@ + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + */ + +-#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) ++#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) && (!defined(__ANDROID_API__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif +@@ -27,7 +27,7 @@ + #endif + #endif + +-#ifdef __APPLE__ ++#if defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) + // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions + #define FOPEN_FUNC(filename, mode) fopen(filename, mode) + #define FTELLO_FUNC(stream) ftello(stream) +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + + #ifdef _WIN32 + # include +@@ -97,7 +98,7 @@ void change_file_date(filename,dosdate,tmu_date) + SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); + CloseHandle(hFile); + #else +-#ifdef unix || __APPLE__ ++#if defined(unix) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) + struct utimbuf ut; + struct tm newdate; + newdate.tm_sec = tmu_date.tm_sec; +@@ -125,11 +126,9 @@ int mymkdir(dirname) + const char* dirname; + { + int ret=0; +-#ifdef _WIN32 ++#if defined(_WIN32) + ret = _mkdir(dirname); +-#elif unix +- ret = mkdir (dirname,0775); +-#elif __APPLE__ ++#elif defined(unix) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) + ret = mkdir (dirname,0775); + #endif + return ret; +diff --git a/third_party/zlib/contrib/minizip/minizip.c b/third_party/zlib/contrib/minizip/minizip.c +index 4288962ecef0..b794953c5c23 100644 +--- a/third_party/zlib/contrib/minizip/minizip.c ++++ b/third_party/zlib/contrib/minizip/minizip.c +@@ -12,8 +12,7 @@ + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + */ + +- +-#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) ++#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) && (!defined(__ANDROID_API__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif +@@ -28,7 +27,7 @@ + #endif + #endif + +-#ifdef __APPLE__ ++#if defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) + // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions + #define FOPEN_FUNC(filename, mode) fopen(filename, mode) + #define FTELLO_FUNC(stream) ftello(stream) +@@ -94,7 +93,7 @@ uLong filetime(f, tmzip, dt) + return ret; + } + #else +-#ifdef unix || __APPLE__ ++#if defined(unix) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__ANDROID_API__) + uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ +-- +2.31.1.818.g46aad6cb9e-goog + diff --git a/deps/zlib/patches/0009-infcover-oob.patch b/deps/zlib/patches/0009-infcover-oob.patch new file mode 100644 index 00000000000000..648360f332d49c --- /dev/null +++ b/deps/zlib/patches/0009-infcover-oob.patch @@ -0,0 +1,24 @@ +From 75690b2683667be5535ac6243438115dc9c40f6a Mon Sep 17 00:00:00 2001 +From: Florian Mayer +Date: Wed, 16 Mar 2022 16:38:36 -0700 +Subject: [PATCH] Fix out of bounds in infcover.c. + +--- + test/infcover.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/test/infcover.c b/test/infcover.c +index 2be01646c..a6d83693c 100644 +--- a/test/infcover.c ++++ b/test/infcover.c +@@ -373,7 +373,9 @@ local void cover_support(void) + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; +- ret = inflateInit_(&strm, ZLIB_VERSION - 1, (int)sizeof(z_stream)); ++ char versioncpy[] = ZLIB_VERSION; ++ versioncpy[0] -= 1; ++ ret = inflateInit_(&strm, versioncpy, (int)sizeof(z_stream)); + assert(ret == Z_VERSION_ERROR); + mem_done(&strm, "wrong version"); + diff --git a/deps/zlib/simd_stub.c b/deps/zlib/simd_stub.c deleted file mode 100644 index c6d46051498f36..00000000000000 --- a/deps/zlib/simd_stub.c +++ /dev/null @@ -1,35 +0,0 @@ -/* simd_stub.c -- stub implementations -* Copyright (C) 2014 Intel Corporation -* For conditions of distribution and use, see copyright notice in zlib.h -*/ -#include - -#include "deflate.h" -#include "x86.h" - -int ZLIB_INTERNAL x86_cpu_enable_simd = 0; - -void ZLIB_INTERNAL crc_fold_init(deflate_state *const s) { - assert(0); -} - -void ZLIB_INTERNAL crc_fold_copy(deflate_state *const s, - unsigned char *dst, - const unsigned char *src, - long len) { - assert(0); -} - -unsigned ZLIB_INTERNAL crc_fold_512to32(deflate_state *const s) { - assert(0); - return 0; -} - -void ZLIB_INTERNAL fill_window_sse(deflate_state *s) -{ - assert(0); -} - -void x86_check_features(void) -{ -} diff --git a/deps/zlib/win32/zlib.def b/deps/zlib/win32/zlib.def deleted file mode 100644 index a2188b000621c4..00000000000000 --- a/deps/zlib/win32/zlib.def +++ /dev/null @@ -1,94 +0,0 @@ -; zlib data compression library -EXPORTS -; basic functions - zlibVersion - deflate - deflateEnd - inflate - inflateEnd -; advanced functions - deflateSetDictionary - deflateGetDictionary - deflateCopy - deflateReset - deflateParams - deflateTune - deflateBound - deflatePending - deflatePrime - deflateSetHeader - inflateSetDictionary - inflateGetDictionary - inflateSync - inflateCopy - inflateReset - inflateReset2 - inflatePrime - inflateMark - inflateGetHeader - inflateBack - inflateBackEnd - zlibCompileFlags -; utility functions - compress - compress2 - compressBound - uncompress - uncompress2 - gzopen - gzdopen - gzbuffer - gzsetparams - gzread - gzfread - gzwrite - gzfwrite - gzprintf - gzvprintf - gzputs - gzgets - gzputc - gzgetc - gzungetc - gzflush - gzseek - gzrewind - gztell - gzoffset - gzeof - gzdirect - gzclose - gzclose_r - gzclose_w - gzerror - gzclearerr -; large file functions - gzopen64 - gzseek64 - gztell64 - gzoffset64 - adler32_combine64 - crc32_combine64 -; checksum functions - adler32 - adler32_z - crc32 - crc32_z - adler32_combine - crc32_combine -; various hacks, don't look :) - deflateInit_ - deflateInit2_ - inflateInit_ - inflateInit2_ - inflateBackInit_ - gzgetc_ - zError - inflateSyncPoint - get_crc_table - inflateUndermine - inflateValidate - inflateCodesUsed - inflateResetKeep - deflateResetKeep - gzopen_w diff --git a/deps/zlib/x86.c b/deps/zlib/x86.c deleted file mode 100644 index 7488ad08b976c8..00000000000000 --- a/deps/zlib/x86.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * x86 feature check - * - * Copyright (C) 2013 Intel Corporation. All rights reserved. - * Author: - * Jim Kukunas - * - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "x86.h" -#include "zutil.h" - -int ZLIB_INTERNAL x86_cpu_enable_ssse3 = 0; -int ZLIB_INTERNAL x86_cpu_enable_simd = 0; - -#ifndef _MSC_VER -#include - -pthread_once_t cpu_check_inited_once = PTHREAD_ONCE_INIT; -static void _x86_check_features(void); - -void x86_check_features(void) -{ - pthread_once(&cpu_check_inited_once, _x86_check_features); -} - -static void _x86_check_features(void) -{ - int x86_cpu_has_sse2; - int x86_cpu_has_ssse3; - int x86_cpu_has_sse42; - int x86_cpu_has_pclmulqdq; - unsigned eax, ebx, ecx, edx; - - eax = 1; -#ifdef __i386__ - __asm__ __volatile__ ( - "xchg %%ebx, %1\n\t" - "cpuid\n\t" - "xchg %1, %%ebx\n\t" - : "+a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx) - ); -#else - __asm__ __volatile__ ( - "cpuid\n\t" - : "+a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) - ); -#endif /* (__i386__) */ - - x86_cpu_has_sse2 = edx & 0x4000000; - x86_cpu_has_ssse3 = ecx & 0x000200; - x86_cpu_has_sse42 = ecx & 0x100000; - x86_cpu_has_pclmulqdq = ecx & 0x2; - - x86_cpu_enable_ssse3 = x86_cpu_has_ssse3; - - x86_cpu_enable_simd = x86_cpu_has_sse2 && - x86_cpu_has_sse42 && - x86_cpu_has_pclmulqdq; -} -#else -#include -#include - -static BOOL CALLBACK _x86_check_features(PINIT_ONCE once, - PVOID param, - PVOID *context); -static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT; - -void x86_check_features(void) -{ - InitOnceExecuteOnce(&cpu_check_inited_once, _x86_check_features, - NULL, NULL); -} - -static BOOL CALLBACK _x86_check_features(PINIT_ONCE once, - PVOID param, - PVOID *context) -{ - int x86_cpu_has_sse2; - int x86_cpu_has_ssse3; - int x86_cpu_has_sse42; - int x86_cpu_has_pclmulqdq; - int regs[4]; - - __cpuid(regs, 1); - - x86_cpu_has_sse2 = regs[3] & 0x4000000; - x86_cpu_has_ssse3 = regs[2] & 0x000200; - x86_cpu_has_sse42 = regs[2] & 0x100000; - x86_cpu_has_pclmulqdq = regs[2] & 0x2; - - x86_cpu_enable_ssse3 = x86_cpu_has_ssse3; - - x86_cpu_enable_simd = x86_cpu_has_sse2 && - x86_cpu_has_sse42 && - x86_cpu_has_pclmulqdq; - return TRUE; -} -#endif /* _MSC_VER */ diff --git a/deps/zlib/x86.h b/deps/zlib/x86.h deleted file mode 100644 index 7205d50265c356..00000000000000 --- a/deps/zlib/x86.h +++ /dev/null @@ -1,16 +0,0 @@ -/* x86.h -- check for x86 CPU features -* Copyright (C) 2013 Intel Corporation Jim Kukunas -* For conditions of distribution and use, see copyright notice in zlib.h -*/ - -#ifndef X86_H -#define X86_H - -#include "zlib.h" - -extern int x86_cpu_enable_ssse3; -extern int x86_cpu_enable_simd; - -void x86_check_features(void); - -#endif /* X86_H */ diff --git a/deps/zlib/zconf.h b/deps/zlib/zconf.h index 353fd36f17e3e5..2d1d03ff951c92 100644 --- a/deps/zlib/zconf.h +++ b/deps/zlib/zconf.h @@ -17,7 +17,7 @@ #if !defined(CHROMIUM_ZLIB_NO_CHROMECONF) /* This include does prefixing as below, but with an updated set of names. Also * sets up export macros in component builds. */ -//#include "chromeconf.h" +#include "chromeconf.h" #endif /* diff --git a/deps/zlib/zlib.gyp b/deps/zlib/zlib.gyp index b6bebb6cf4e6a4..52b4925b0e52de 100644 --- a/deps/zlib/zlib.gyp +++ b/deps/zlib/zlib.gyp @@ -65,7 +65,7 @@ 'USE_FILE32API' ], }], - ['(target_arch in "ia32 x64" and OS!="ios") or arm_fpu=="neon"', { + ['(target_arch in "ia32 x64 x32" and OS!="ios") or arm_fpu=="neon"', { 'sources': [ 'adler32_simd.c', 'adler32_simd.h', @@ -78,7 +78,7 @@ 'sources': [ 'inflate.c', ], }], # Incorporate optimizations where possible - ['target_arch in "ia32 x64" and OS!="ios"', { + ['target_arch in "ia32 x64 x32" and OS!="ios"', { 'defines': [ 'ADLER32_SIMD_SSSE3', 'INFLATE_CHUNK_SIMD_SSE2', @@ -163,4 +163,4 @@ ], }], ], -} +} \ No newline at end of file diff --git a/deps/zlib/zlib.h b/deps/zlib/zlib.h index 99fd467f6b1a54..35e829b70d146e 100644 --- a/deps/zlib/zlib.h +++ b/deps/zlib/zlib.h @@ -712,11 +712,12 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression approach (which is a function of the level) or the - strategy is changed, and if any input has been consumed in a previous - deflate() call, then the input available so far is compressed with the old - level and strategy using deflate(strm, Z_BLOCK). There are three approaches - for the compression levels 0, 1..3, and 4..9 respectively. The new level - and strategy will take effect at the next call of deflate(). + strategy is changed, and if there have been any deflate() calls since the + state was initialized or reset, then the input available so far is + compressed with the old level and strategy using deflate(strm, Z_BLOCK). + There are three approaches for the compression levels 0, 1..3, and 4..9 + respectively. The new level and strategy will take effect at the next call + of deflate(). If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does not have enough output space to complete, then the parameter change will not @@ -1916,7 +1917,7 @@ ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); -#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +#if defined(_WIN32) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif diff --git a/deps/zlib/zutil.c b/deps/zlib/zutil.c index a76c6b0c7e557f..dcab28a0d5177a 100644 --- a/deps/zlib/zutil.c +++ b/deps/zlib/zutil.c @@ -136,8 +136,8 @@ const char * ZEXPORT zError(err) return ERR_MSG(err); } -#if defined(_WIN32_WCE) - /* The Microsoft C Run-Time Library for Windows CE doesn't have +#if defined(_WIN32_WCE) && _WIN32_WCE < 0x800 + /* The older Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */