deps: update zlib to upstream 8bbd6c31

Updated as described in doc/contributing/maintaining-zlib.md.

PR-URL: https://github.com/nodejs/node/pull/45387
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
This commit is contained in:
Luigi Pinca 2022-11-09 08:36:52 +01:00 committed by Node.js GitHub Bot
parent 8f9dc2732e
commit b6c108f092
111 changed files with 18352 additions and 3484 deletions

View File

@ -1237,6 +1237,7 @@ def configure_node(o):
# Enable branch protection for arm64
if target_arch == 'arm64':
o['cflags']+=['-msign-return-address=all']
o['variables']['arm_fpu'] = options.arm_fpu or 'neon'
if options.node_snapshot_main is not None:
if options.shared:

309
deps/zlib/BUILD.gn vendored
View File

@ -1,9 +1,13 @@
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Copyright 2013 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/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,49 @@ 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") {
visibility = [ ":*" ]
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 +72,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 +103,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 +123,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 +141,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" ]
cflags_c = [ "-march=armv8-a+aes+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 +172,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 +191,19 @@ 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" ]
configs += [ ":zlib_warnings" ]
public_configs = [ ":zlib_inflate_chunk_simd_config" ]
public_deps = [ ":zlib_common_headers" ]
}
config("zlib_crc32_simd_config") {
@ -188,6 +219,7 @@ source_set("zlib_crc32_simd") {
sources = [
"crc32_simd.c",
"crc32_simd.h",
"crc_folding.c",
]
if (!is_win || is_clang) {
@ -201,39 +233,45 @@ source_set("zlib_crc32_simd") {
configs += [ ":zlib_internal_config" ]
public_configs = [ ":zlib_crc32_simd_config" ]
public_deps = [ ":zlib_common_headers" ]
}
source_set("zlib_x86_simd") {
config("zlib_slide_hash_simd_config") {
if (use_x86_x64_optimizations) {
defines = [ "DEFLATE_SLIDE_HASH_SSE2" ]
}
if (use_arm_neon_optimizations) {
defines = [ "DEFLATE_SLIDE_HASH_NEON" ]
}
}
source_set("zlib_slide_hash_simd") {
visibility = [ ":*" ]
if (use_x86_x64_optimizations) {
sources = [
"crc_folding.c",
"fill_window_sse.c",
]
if (!is_win || is_clang) {
cflags = [
"-msse4.2",
"-mpclmul",
]
}
} else {
sources = [
"simd_stub.c",
]
sources = [ "slide_hash_simd.h" ]
}
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
":zlib_internal_config",
"//build/config/compiler:no_chromium_code",
]
if (use_arm_neon_optimizations) {
sources = [ "slide_hash_simd.h" ]
}
configs += [ ":zlib_internal_config" ]
public_configs = [ ":zlib_slide_hash_simd_config" ]
public_deps = [ ":zlib_common_headers" ]
}
config("zlib_warnings") {
if (is_clang && use_x86_x64_optimizations) {
cflags = [ "-Wno-incompatible-pointer-types" ]
if (is_clang) {
cflags = [
"-Wno-deprecated-non-prototype",
"-Wno-incompatible-pointer-types",
"-Wunused-variable",
]
}
}
@ -248,6 +286,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 +307,6 @@ component("zlib") {
"trees.c",
"trees.h",
"uncompr.c",
"x86.h",
"zconf.h",
"zlib.h",
"zutil.c",
@ -277,35 +316,57 @@ 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",
":zlib_inflate_chunk_simd",
":zlib_slide_hash_simd",
]
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" ]
deps += [ ":zlib_arm_crc32" ]
}
} else {
sources += [ "inflate.c" ]
}
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
}
@ -313,8 +374,11 @@ config("minizip_warnings") {
visibility = [ ":*" ]
if (is_clang) {
# zlib uses `if ((a == b))` for some reason.
cflags = [ "-Wno-parentheses-equality" ]
cflags = [
# zlib uses `if ((a == b))` for some reason.
"-Wno-parentheses-equality",
"-Wno-deprecated-non-prototype",
]
}
}
@ -337,43 +401,120 @@ 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",
"-Wno-deprecated-non-prototype",
]
}
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",
"-Wno-deprecated-non-prototype",
]
}
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",
]
}
}

3
deps/zlib/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,3 @@
monorail: {
component: "Internals"
}

4
deps/zlib/LICENSE vendored
View File

@ -1,6 +1,6 @@
version 1.2.11, January 15th, 2017
version 1.2.12, March 27th, 2022
Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages

6
deps/zlib/OWNERS vendored
View File

@ -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

View File

@ -1,7 +1,8 @@
Name: zlib
Short Name: zlib
URL: http://zlib.net/
Version: 1.2.11
Version: 1.2.13
CPEPrefix: cpe:/a:zlib:zlib:1.2.13
Security Critical: yes
License: Custom license
License File: LICENSE
@ -26,3 +27,6 @@ Local Modifications:
- Plus the changes in 'patches' folder.
- Code in contrib/ other than contrib/minizip was added to match zlib's
contributor layout.
- In sync with 1.2.13 official release
- ZIP reader modified to allow for progress callbacks during extraction.
- ZIP reader modified to add detection of AES encrypted content.

12
deps/zlib/adler32.c vendored
View File

@ -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

View File

@ -1,6 +1,6 @@
/* adler32_simd.c
*
* Copyright 2017 The Chromium Authors. All rights reserved.
* Copyright 2017 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*
@ -50,13 +50,9 @@
#define NMAX 5552
#if defined(ADLER32_SIMD_SSSE3)
#ifndef __GNUC__
#define __attribute__()
#endif
#include <tmmintrin.h>
__attribute__((target("ssse3")))
uint32_t ZLIB_INTERNAL adler32_simd_( /* SSSE3 */
uint32_t adler,
const unsigned char *buf,

View File

@ -1,6 +1,6 @@
/* adler32_simd.h
*
* Copyright 2017 The Chromium Authors. All rights reserved.
* Copyright 2017 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/

View File

@ -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 <stdint.h>
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 <pthread.h>
#endif
#if defined(ARMV8_OS_ANDROID)
#include <cpu-features.h>
#elif defined(ARMV8_OS_LINUX)
#include <asm/hwcap.h>
#include <sys/auxv.h>
#elif defined(ARMV8_OS_FUCHSIA)
#include <zircon/features.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#elif defined(ARMV8_OS_WINDOWS)
#include <windows.h>
#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
}

View File

@ -1,13 +0,0 @@
/* arm_features.h -- 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 "zlib.h"
extern int arm_cpu_enable_crc32;
extern int arm_cpu_enable_pmull;
void arm_check_features(void);

View File

@ -1,4 +1,4 @@
/* Copyright 2017 The Chromium Authors. All rights reserved.
/* Copyright 2017 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
@ -49,6 +49,9 @@
#define crc32 Cr_z_crc32
#define crc32_combine Cr_z_crc32_combine
#define crc32_combine64 Cr_z_crc32_combine64
#define crc32_combine_gen64 Cr_z_crc32_combine_gen64
#define crc32_combine_gen Cr_z_crc32_combine_gen
#define crc32_combine_op Cr_z_crc32_combine_op
#define crc32_z Cr_z_crc32_z
#define deflate Cr_z_deflate
#define deflateBound Cr_z_deflateBound
@ -191,5 +194,10 @@
#define arm_cpu_enable_pmull Cr_z_arm_cpu_enable_pmull
#define arm_check_features Cr_z_arm_check_features
#define armv8_crc32_little Cr_z_armv8_crc32_little
#define armv8_crc32_pmull_little Cr_z_armv8_crc32_pmull_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_ */

View File

@ -19,7 +19,7 @@
memory, Z_BUF_ERROR if there was not enough room in the output buffer,
Z_STREAM_ERROR if the level parameter is invalid.
*/
int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
int ZEXPORT compress2(dest, destLen, source, sourceLen, level)
Bytef *dest;
uLongf *destLen;
const Bytef *source;
@ -65,7 +65,7 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
/* ===========================================================================
*/
int ZEXPORT compress (dest, destLen, source, sourceLen)
int ZEXPORT compress(dest, destLen, source, sourceLen)
Bytef *dest;
uLongf *destLen;
const Bytef *source;
@ -78,7 +78,7 @@ int ZEXPORT compress (dest, destLen, source, sourceLen)
If the default memLevel or windowBits for deflateInit() is changed, then
this function needs to be updated.
*/
uLong ZEXPORT compressBound (sourceLen)
uLong ZEXPORT compressBound(sourceLen)
uLong sourceLen;
{
sourceLen = sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +

35
deps/zlib/contrib/bench/check.sh vendored Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
#
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the chromium source repository LICENSE file.
#
# Given a zlib_bench executable and some data files, run zlib_bench --check
# over those data files, for all zlib types (gzip|zlib|raw) and compression
# levels 1..9 for each type. Example:
#
# check.sh ./out/Release/zlib_bench [--check-binary] ~/snappy/testdata/*
#
# The --check-binary option modifies --check output: the compressed data is
# also written to the program output.
ZLIB_BENCH="$1" && shift
CHECK_TYPE="--check"
if [[ "${1}" == "--check-binary" ]]; then
CHECK_TYPE="$1" && shift # output compressed data too
fi
DATA_FILES="$*"
echo ${ZLIB_BENCH} | grep -E "/(zlib_bench|a.out)$" > /dev/null
if [[ $? != 0 ]] || [[ -z "${DATA_FILES}" ]]; then
echo "usage: check.sh zlib_bench [--check-binary] files ..." >&2
exit 1;
fi
for type in gzip zlib raw; do
for level in $(seq 1 9); do
${ZLIB_BENCH} $type --compression $level ${CHECK_TYPE} ${DATA_FILES}
done
done

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 The Chromium Authors. All rights reserved.
* Copyright 2018 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*
@ -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 <algorithm>
@ -38,13 +38,14 @@ 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 {
Data(size_t s) { data.reset(new (std::nothrow) char[size = s]); }
std::unique_ptr<char[]> data;
size_t size;
std::string name;
};
Data read_file_data_or_exit(const char* name) {
@ -66,6 +67,7 @@ Data read_file_data_or_exit(const char* name) {
exit(1);
}
data.name = std::string(name);
return data;
}
@ -99,10 +101,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_compression_level;
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 = Z_DEFAULT_COMPRESSION;
void zlib_compress(
const zlib_wrapper type,
@ -119,7 +136,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);
@ -129,6 +146,8 @@ void zlib_compress(
stream.avail_in = (uInt)input_size;
result = deflate(&stream, Z_FINISH);
if (stream.avail_in > 0)
error_exit("compress: input was not consumed", Z_DATA_ERROR);
if (result == Z_STREAM_END)
output_size = stream.total_out;
result |= deflateEnd(&stream);
@ -178,14 +197,67 @@ 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 check_file(const Data& file, zlib_wrapper type, int mode) {
printf("%s %d %s%s\n", zlib_wrapper_name(type), zlib_compression_level,
zlib_level_strategy_name(zlib_compression_level), file.name.c_str());
// Compress the file data.
std::string compressed;
zlib_compress(type, file.data.get(), file.size, &compressed, true);
// Output compressed data integrity check: the data crc32.
unsigned long check = crc32_z(0, Z_NULL, 0);
const Bytef* data = (const Bytef*)compressed.data();
static_assert(sizeof(z_size_t) == sizeof(size_t), "z_size_t size");
check = crc32_z(check, data, (z_size_t)compressed.size());
const size_t compressed_length = compressed.size();
printf("data crc32 %.8lx length %zu\n", check, compressed_length);
// Output gzip or zlib DEFLATE stream internal check data.
if (type == kWrapperGZIP) {
uint32_t prev_word, last_word;
data += compressed_length - 8;
prev_word = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
data += 4; // last compressed data word
last_word = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
printf("gzip crc32 %.8x length %u\n", prev_word, last_word);
} else if (type == kWrapperZLIB) {
uint32_t last_word;
data += compressed_length - 4;
last_word = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
printf("zlib adler %.8x\n", last_word);
}
if (mode == 2) // --check-binary: output compressed data.
fwrite(compressed.data(), compressed_length, 1, stdout);
if (fflush(stdout), ferror(stdout))
error_exit("check file: error writing output", 3);
}
void zlib_file(const char* name, zlib_wrapper type, int width, int check) {
/*
* Read the file data.
*/
const auto file = read_file_data_or_exit(name);
struct Data file = read_file_data_or_exit(name);
const int length = static_cast<int>(file.size);
const char* data = file.data.get();
printf("%-40s :\n", name);
/*
* Compress file: report output data checks and return.
*/
if (check) {
file.name = file.name.substr(file.name.find_last_of("/\\") + 1);
check_file(file, type, check);
return;
}
/*
* 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 +335,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<unsigned>(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 +348,25 @@ 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] [--check]";
printf("usage: %s %s files ...\n", program, options);
printf("zlib version: %s\n", ZLIB_VERSION);
exit(1);
}
@ -302,15 +381,35 @@ 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 size_field_width = 0;
int file_check = 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, "--huffman")) {
zlib_strategy = Z_HUFFMAN_ONLY;
} else if (get_option(argc, argv, "--rle")) {
zlib_strategy = Z_RLE;
} else if (get_option(argc, argv, "--check")) {
file_check = 1;
} else if (get_option(argc, argv, "--check-binary")) {
file_check = 2;
} else if (get_option(argc, argv, "--field")) {
get_field_width(argc, argv, size_field_width);
} else {
usage_exit(argv[0]);
}
}
if (argn >= argc)
usage_exit(argv[0]);
if (size_field_width < 6)
size_field_width = 6;
while (argn < argc)
zlib_file(argv[argn++], type);
zlib_file(argv[argn++], type, size_field_width, file_check);
return 0;
}

View File

@ -0,0 +1,15 @@
Name: ZIP file API for reading file entries in a ZIP archive
Short Name: minizip
URL: https://github.com/madler/zlib/tree/master/contrib/minizip
Version: 1.2.12
License: Zlib
Security Critical: yes
Description:
Minizip provides API on top of zlib that can enumerate and extract ZIP archive
files. See minizip.md for chromium build instructions.
Local Modifications:
- Add parsing of the 'Info-ZIP Unicode Path Extra Field' as described in
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT section 4.6.9.
(see crrev.com/1002476)

View File

@ -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));

View File

@ -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 <time.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef _WIN32
# include <direct.h>
@ -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;

View File

@ -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 */

9
deps/zlib/contrib/minizip/minizip.md vendored Normal file
View File

@ -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

View File

@ -1023,46 +1023,102 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file,
while(acc < file_info.size_file_extra)
{
uLong headerId;
uLong dataSize;
uLong dataSize;
if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK)
err=UNZ_ERRNO;
if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK)
err=UNZ_ERRNO;
/* ZIP64 extra fields */
if (headerId == 0x0001)
{
uLong uL;
uLong uL;
if(file_info.uncompressed_size == MAXU32)
{
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
err=UNZ_ERRNO;
}
if(file_info.uncompressed_size == MAXU32)
{
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
err=UNZ_ERRNO;
}
if(file_info.compressed_size == MAXU32)
{
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
err=UNZ_ERRNO;
}
if(file_info.compressed_size == MAXU32)
{
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
err=UNZ_ERRNO;
}
if(file_info_internal.offset_curfile == MAXU32)
{
/* Relative Header offset */
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
err=UNZ_ERRNO;
}
if(file_info_internal.offset_curfile == MAXU32)
{
/* Relative Header offset */
if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
err=UNZ_ERRNO;
}
if(file_info.disk_num_start == MAXU32)
{
/* Disk Start Number */
if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK)
err=UNZ_ERRNO;
}
if(file_info.disk_num_start == MAXU32)
{
/* Disk Start Number */
if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK)
err=UNZ_ERRNO;
}
}
else if (headerId == 0x7075) /* Info-ZIP Unicode Path Extra Field */
{
int version = 0;
if (unz64local_getByte(&s->z_filefunc, s->filestream, &version) != UNZ_OK)
{
err = UNZ_ERRNO;
}
if (version != 1)
{
if (ZSEEK64(s->z_filefunc, s->filestream,dataSize - 1, ZLIB_FILEFUNC_SEEK_CUR) != 0)
{
err = UNZ_ERRNO;
}
}
else
{
uLong uCrc, uHeaderCrc, fileNameSize;
if (unz64local_getLong(&s->z_filefunc, s->filestream, &uCrc) != UNZ_OK)
{
err = UNZ_ERRNO;
}
uHeaderCrc = crc32(0, (const unsigned char *)szFileName, file_info.size_filename);
fileNameSize = dataSize - (2 * sizeof (short) + 1);
/* Check CRC against file name in the header. */
if (uHeaderCrc != uCrc)
{
if (ZSEEK64(s->z_filefunc, s->filestream, fileNameSize, ZLIB_FILEFUNC_SEEK_CUR) != 0)
{
err = UNZ_ERRNO;
}
}
else
{
uLong uSizeRead;
if (fileNameSize < fileNameBufferSize)
{
*(szFileName + fileNameSize) = '\0';
uSizeRead = fileNameSize;
}
else
{
uSizeRead = fileNameBufferSize;
}
if ((fileNameSize > 0) && (fileNameBufferSize > 0))
{
if (ZREAD64(s->z_filefunc, s->filestream, szFileName, uSizeRead) != uSizeRead)
{
err = UNZ_ERRNO;
}
}
}
}
}
else
{
if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0)

View File

@ -1,6 +1,6 @@
/* chunkcopy.h -- fast chunk copy and set operations
* Copyright (C) 2017 ARM, Inc.
* Copyright 2017 The Chromium Authors. All rights reserved.
* Copyright 2017 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
@ -29,13 +29,23 @@
#include <arm_neon.h>
typedef uint8x16_t z_vec128i_t;
#elif defined(INFLATE_CHUNK_SIMD_SSE2)
#pragma GCC target ("sse2")
#include <emmintrin.h>
typedef __m128i z_vec128i_t;
#else
#error chunkcopy.h inflate chunk SIMD is not defined for your build target
#endif
/*
* Suppress MSan errors about copying uninitialized bytes (crbug.com/1376033).
*/
#define Z_DISABLE_MSAN
#if defined(__has_feature)
#if __has_feature(memory_sanitizer)
#undef Z_DISABLE_MSAN
#define Z_DISABLE_MSAN __attribute__((no_sanitize("memory")))
#endif
#endif
/*
* chunk copy type: the z_vec128i_t type size should be exactly 128-bits
* and equal to CHUNKCOPY_CHUNK_SIZE.
@ -83,7 +93,7 @@ static inline void storechunk(
static inline unsigned char FAR* chunkcopy_core(
unsigned char FAR* out,
const unsigned char FAR* from,
unsigned len) {
unsigned len) Z_DISABLE_MSAN {
const int bump = (--len % CHUNKCOPY_CHUNK_SIZE) + 1;
storechunk(out, loadchunk(from));
out += bump;
@ -113,6 +123,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;
@ -149,7 +163,7 @@ static inline unsigned char FAR* chunkcopy_core_safe(
static inline unsigned char FAR* chunkunroll_relaxed(
unsigned char FAR* out,
unsigned FAR* dist,
unsigned FAR* len) {
unsigned FAR* len) Z_DISABLE_MSAN {
const unsigned char FAR* from = out - *dist;
while (*dist < *len && *dist < CHUNKCOPY_CHUNK_SIZE) {
storechunk(out, loadchunk(from));
@ -339,6 +353,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 +379,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 +430,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
@ -441,5 +484,6 @@ typedef unsigned long inflate_holder_t;
#undef Z_STATIC_ASSERT
#undef Z_RESTRICT
#undef Z_BUILTIN_MEMCPY
#undef Z_DISABLE_MSAN
#endif /* CHUNKCOPY_H */

View File

@ -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 */
@ -339,7 +339,6 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */
state->bits = bits;
Assert((state->hold >> state->bits) == 0, "invalid input data state");
return;
}
/*

View File

@ -1,5 +1,5 @@
/* inflate.c -- zlib decompression
* Copyright (C) 1995-2016 Mark Adler
* Copyright (C) 1995-2022 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -131,6 +131,7 @@ z_streamp strm;
state->mode = HEAD;
state->last = 0;
state->havedict = 0;
state->flags = -1;
state->dmax = 32768U;
state->head = Z_NULL;
state->hold = 0;
@ -168,6 +169,8 @@ int windowBits;
/* extract wrap request from windowBits parameter */
if (windowBits < 0) {
if (windowBits < -15)
return Z_STREAM_ERROR;
wrap = 0;
windowBits = -windowBits;
}
@ -459,10 +462,10 @@ unsigned copy;
/* check function to use adler32() for zlib or crc32() for gzip */
#ifdef GUNZIP
# define UPDATE(check, buf, len) \
# define UPDATE_CHECK(check, buf, len) \
(state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
#else
# define UPDATE(check, buf, len) adler32(check, buf, len)
# define UPDATE_CHECK(check, buf, len) adler32(check, buf, len)
#endif
/* check macros for header crc */
@ -682,7 +685,6 @@ int flush;
state->mode = FLAGS;
break;
}
state->flags = 0; /* expect zlib header */
if (state->head != Z_NULL)
state->head->done = -1;
if (!(state->wrap & 1) || /* check if zlib header allowed */
@ -709,6 +711,7 @@ int flush;
break;
}
state->dmax = 1U << len;
state->flags = 0; /* indicate zlib header */
Tracev((stderr, "inflate: zlib header ok\n"));
strm->adler = state->check = adler32(0L, Z_NULL, 0);
state->mode = hold & 0x200 ? DICTID : TYPE;
@ -734,6 +737,7 @@ int flush;
CRC2(state->check, hold);
INITBITS();
state->mode = TIME;
/* fallthrough */
case TIME:
NEEDBITS(32);
if (state->head != Z_NULL)
@ -742,6 +746,7 @@ int flush;
CRC4(state->check, hold);
INITBITS();
state->mode = OS;
/* fallthrough */
case OS:
NEEDBITS(16);
if (state->head != Z_NULL) {
@ -752,6 +757,7 @@ int flush;
CRC2(state->check, hold);
INITBITS();
state->mode = EXLEN;
/* fallthrough */
case EXLEN:
if (state->flags & 0x0400) {
NEEDBITS(16);
@ -765,14 +771,16 @@ int flush;
else if (state->head != Z_NULL)
state->head->extra = Z_NULL;
state->mode = EXTRA;
/* fallthrough */
case EXTRA:
if (state->flags & 0x0400) {
copy = state->length;
if (copy > have) copy = have;
if (copy) {
if (state->head != Z_NULL &&
state->head->extra != Z_NULL) {
len = state->head->extra_len - state->length;
state->head->extra != Z_NULL &&
(len = state->head->extra_len - state->length) <
state->head->extra_max) {
zmemcpy(state->head->extra + len, next,
len + copy > state->head->extra_max ?
state->head->extra_max - len : copy);
@ -787,6 +795,7 @@ int flush;
}
state->length = 0;
state->mode = NAME;
/* fallthrough */
case NAME:
if (state->flags & 0x0800) {
if (have == 0) goto inf_leave;
@ -808,6 +817,7 @@ int flush;
state->head->name = Z_NULL;
state->length = 0;
state->mode = COMMENT;
/* fallthrough */
case COMMENT:
if (state->flags & 0x1000) {
if (have == 0) goto inf_leave;
@ -828,6 +838,7 @@ int flush;
else if (state->head != Z_NULL)
state->head->comment = Z_NULL;
state->mode = HCRC;
/* fallthrough */
case HCRC:
if (state->flags & 0x0200) {
NEEDBITS(16);
@ -851,6 +862,7 @@ int flush;
strm->adler = state->check = ZSWAP32(hold);
INITBITS();
state->mode = DICT;
/* fallthrough */
case DICT:
if (state->havedict == 0) {
RESTORE();
@ -858,8 +870,10 @@ int flush;
}
strm->adler = state->check = adler32(0L, Z_NULL, 0);
state->mode = TYPE;
/* fallthrough */
case TYPE:
if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
/* fallthrough */
case TYPEDO:
if (state->last) {
BYTEBITS();
@ -910,8 +924,10 @@ int flush;
INITBITS();
state->mode = COPY_;
if (flush == Z_TREES) goto inf_leave;
/* fallthrough */
case COPY_:
state->mode = COPY;
/* fallthrough */
case COPY:
copy = state->length;
if (copy) {
@ -947,6 +963,7 @@ int flush;
Tracev((stderr, "inflate: table sizes ok\n"));
state->have = 0;
state->mode = LENLENS;
/* fallthrough */
case LENLENS:
while (state->have < state->ncode) {
NEEDBITS(3);
@ -968,6 +985,7 @@ int flush;
Tracev((stderr, "inflate: code lengths ok\n"));
state->have = 0;
state->mode = CODELENS;
/* fallthrough */
case CODELENS:
while (state->have < state->nlen + state->ndist) {
for (;;) {
@ -1027,11 +1045,11 @@ int flush;
}
/* build code tables -- note: do not change the lenbits or distbits
values here (9 and 6) without reading the comments in inftrees.h
values here (10 and 9) without reading the comments in inftrees.h
concerning the ENOUGH constants, which depend on those values */
state->next = state->codes;
state->lencode = (const code FAR *)(state->next);
state->lenbits = 9;
state->lenbits = 10;
ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
&(state->lenbits), state->work);
if (ret) {
@ -1040,7 +1058,7 @@ int flush;
break;
}
state->distcode = (const code FAR *)(state->next);
state->distbits = 6;
state->distbits = 9;
ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
&(state->next), &(state->distbits), state->work);
if (ret) {
@ -1051,8 +1069,10 @@ int flush;
Tracev((stderr, "inflate: codes ok\n"));
state->mode = LEN_;
if (flush == Z_TREES) goto inf_leave;
/* fallthrough */
case LEN_:
state->mode = LEN;
/* fallthrough */
case LEN:
if (have >= INFLATE_FAST_MIN_INPUT &&
left >= INFLATE_FAST_MIN_OUTPUT) {
@ -1103,6 +1123,7 @@ int flush;
}
state->extra = (unsigned)(here.op) & 15;
state->mode = LENEXT;
/* fallthrough */
case LENEXT:
if (state->extra) {
NEEDBITS(state->extra);
@ -1113,6 +1134,7 @@ int flush;
Tracevv((stderr, "inflate: length %u\n", state->length));
state->was = state->length;
state->mode = DIST;
/* fallthrough */
case DIST:
for (;;) {
here = state->distcode[BITS(state->distbits)];
@ -1140,6 +1162,7 @@ int flush;
state->offset = (unsigned)here.val;
state->extra = (unsigned)(here.op) & 15;
state->mode = DISTEXT;
/* fallthrough */
case DISTEXT:
if (state->extra) {
NEEDBITS(state->extra);
@ -1156,6 +1179,7 @@ int flush;
#endif
Tracevv((stderr, "inflate: distance %u\n", state->offset));
state->mode = MATCH;
/* fallthrough */
case MATCH:
if (left == 0) goto inf_leave;
copy = out - left;
@ -1214,7 +1238,7 @@ int flush;
state->total += out;
if ((state->wrap & 4) && out)
strm->adler = state->check =
UPDATE(state->check, put - out, out);
UPDATE_CHECK(state->check, put - out, out);
out = left;
if ((state->wrap & 4) && (
#ifdef GUNZIP
@ -1230,10 +1254,11 @@ int flush;
}
#ifdef GUNZIP
state->mode = LENGTH;
/* fallthrough */
case LENGTH:
if (state->wrap && state->flags) {
NEEDBITS(32);
if (hold != (state->total & 0xffffffffUL)) {
if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) {
strm->msg = (char *)"incorrect length check";
state->mode = BAD;
break;
@ -1243,6 +1268,7 @@ int flush;
}
#endif
state->mode = DONE;
/* fallthrough */
case DONE:
ret = Z_STREAM_END;
goto inf_leave;
@ -1252,6 +1278,7 @@ int flush;
case MEM:
return Z_MEM_ERROR;
case SYNC:
/* fallthrough */
default:
return Z_STREAM_ERROR;
}
@ -1263,16 +1290,29 @@ int flush;
Note: a memory error from inflate() is non-recoverable.
*/
inf_leave:
/* We write a defined value in the unused space to help mark
#if defined(ZLIB_DEBUG)
/* XXX(cavalcantii): I put this in place back in 2017 to help debug faulty
* client code relying on undefined behavior when chunk_copy first landed.
*
* It is save to say after all these years that Chromium code is well
* behaved and works fine with the optimization, therefore we can enable
* this only for DEBUG builds.
*
* We write a defined value in the unused space to help mark
* where the stream has ended. We don't use zeros as that can
* mislead clients relying on undefined behavior (i.e. assuming
* that the data is over when the buffer has a zero/null value).
*
* The basic idea is that if client code is not relying on the zlib context
* to inform the amount of decompressed data, but instead reads the output
* buffer until a zero/null is found, it will fail faster and harder
* when the remaining of the buffer is marked with a symbol (e.g. 0x55).
*/
if (left >= CHUNKCOPY_CHUNK_SIZE)
memset(put, 0x55, CHUNKCOPY_CHUNK_SIZE);
else
memset(put, 0x55, left);
#endif
RESTORE();
if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
(state->mode < CHECK || flush != Z_FINISH)))
@ -1287,7 +1327,7 @@ int flush;
state->total += out;
if ((state->wrap & 4) && out)
strm->adler = state->check =
UPDATE(state->check, strm->next_out - out, out);
UPDATE_CHECK(state->check, strm->next_out - out, out);
strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
(state->mode == TYPE ? 128 : 0) +
(state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
@ -1423,6 +1463,7 @@ int ZEXPORT inflateSync(strm)
z_streamp strm;
{
unsigned len; /* number of bytes to look at or looked at */
int flags; /* temporary to save header status */
unsigned long in, out; /* temporary to save total_in and total_out */
unsigned char buf[4]; /* to restore bit buffer to byte string */
struct inflate_state FAR *state;
@ -1455,9 +1496,15 @@ z_streamp strm;
/* return no joy or set up to restart inflate() on a new block */
if (state->have != 4) return Z_DATA_ERROR;
if (state->flags == -1)
state->wrap = 0; /* if no header yet, treat as raw */
else
state->wrap &= ~4; /* no point in computing a check value now */
flags = state->flags;
in = strm->total_in; out = strm->total_out;
inflateReset(strm);
strm->total_in = in; strm->total_out = out;
state->flags = flags;
state->mode = TYPE;
return Z_OK;
}
@ -1553,7 +1600,7 @@ int check;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
state = (struct inflate_state FAR *)strm->state;
if (check)
if (check && state->wrap)
state->wrap |= 4;
else
state->wrap &= ~4;

View File

@ -1,48 +1,61 @@
/* insert_string.h
*
* Copyright 2019 The Chromium Authors. All rights reserved.
* Copyright 2019 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
#ifdef _MSC_VER
#ifndef INSERT_STRING_H
#define INSERT_STRING_H
#ifndef INLINE
#if defined(_MSC_VER) && !defined(__clang__)
#define INLINE __inline
#else
#define INLINE inline
#endif
#endif
#include "cpu_features.h"
/* Optimized insert_string block */
#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32)
#define TARGET_CPU_WITH_CRC
// clang-format off
#if defined(CRC32_SIMD_SSE42_PCLMUL)
/* Required to make MSVC bot build pass. */
#include <smmintrin.h>
#if defined(__GNUC__) || defined(__clang__)
#undef TARGET_CPU_WITH_CRC
#include <smmintrin.h> /* 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
/* CRC32C uint32_t */
#define _cpu_crc32c_hash_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__)
/* CRC32C uint32_t */
#define _cpu_crc32c_hash_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;
@ -52,22 +65,33 @@ local INLINE Pos insert_string_optimized(deflate_state* const s,
if (s->level >= 6)
val &= 0xFFFFFF;
/* Unlike the case of data integrity checks for GZIP format where the
* polynomial used is defined (https://tools.ietf.org/html/rfc1952#page-11),
* here it is just a hash function for the hash table used while
* performing compression.
*/
h = _cpu_crc32_u32(h, val);
/* Compute hash from the CRC32C of |val|. */
h = _cpu_crc32c_hash_u32(h, val);
ret = s->head[h & s->hash_mask];
s->head[h & s->hash_mask] = str;
s->prev[str & s->w_mask] = ret;
return ret;
}
#endif /* Optimized insert_string block */
#endif // TARGET_CPU_WITH_CRC
/**
* Some applications need to match zlib DEFLATE output exactly [3]. Use the
* canonical zlib Rabin-Karp rolling hash [1,2] in that case.
*
* [1] For a description of the Rabin and Karp algorithm, see "Algorithms"
* book by R. Sedgewick, Addison-Wesley, p252.
* [2] https://www.euccas.me/zlib/#zlib_rabin_karp and also "rolling hash"
* https://en.wikipedia.org/wiki/Rolling_hash
* [3] crbug.com/1316541 AOSP incremental client APK package OTA upgrades.
*/
#ifdef CHROMIUM_ZLIB_NO_CASTAGNOLI
#define USE_ZLIB_RABIN_KARP_ROLLING_HASH
#endif
/* ===========================================================================
* Update a hash value with the given input byte
* Update a hash value with the given input byte (Rabin-Karp rolling hash).
* IN assertion: all calls to UPDATE_HASH are made with consecutive input
* characters, so that a running hash key can be computed from the previous
* key instead of complete recalculation each time.
@ -99,24 +123,24 @@ 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: SIMD crc32c symbol hasher
* 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 canonical 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(USE_ZLIB_RABIN_KARP_ROLLING_HASH)
/* So this build-time option can be used to disable the crc32c hash, and use
* the Rabin-Karp hash instead.
*/ /* FALLTHROUGH Rabin-Karp */
#elif defined(TARGET_CPU_WITH_CRC) && defined(CRC32_SIMD_SSE42_PCLMUL)
if (x86_cpu_enable_simd)
return insert_string_optimized(s, str);
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
#endif
return insert_string_c(s, str);
return insert_string_c(s, str); /* Rabin-Karp */
}
#endif /* INSERT_STRING_H */

View File

@ -1,65 +0,0 @@
/* 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.
*/
#ifndef __SLIDE_HASH__NEON__
#define __SLIDE_HASH__NEON__
#include "deflate.h"
#include <arm_neon.h>
inline static void ZLIB_INTERNAL neon_slide_hash_update(Posf *hash,
const uInt hash_size,
const ush w_size)
{
/* NEON 'Q' registers allow to store 128 bits, so we can load 8x16-bits
* values. For further details, check:
* ARM DHT 0002A, section 1.3.2 NEON Registers.
*/
const size_t chunk = sizeof(uint16x8_t) / sizeof(uint16_t);
/* Unrolling the operation yielded a compression performance boost in both
* ARMv7 (from 11.7% to 13.4%) and ARMv8 (from 3.7% to 7.5%) for HTML4
* content. For full benchmarking data, check: http://crbug.com/863257.
*/
const size_t stride = 2*chunk;
const uint16x8_t v = vdupq_n_u16(w_size);
for (Posf *end = hash + hash_size; hash != end; hash += stride) {
uint16x8_t m_low = vld1q_u16(hash);
uint16x8_t m_high = vld1q_u16(hash + chunk);
/* The first 'q' in vqsubq_u16 makes these subtracts saturate to zero,
* replacing the ternary operator expression in the original code:
* (m >= wsize ? m - wsize : NIL).
*/
m_low = vqsubq_u16(m_low, v);
m_high = vqsubq_u16(m_high, v);
vst1q_u16(hash, m_low);
vst1q_u16(hash + chunk, m_high);
}
}
inline static void ZLIB_INTERNAL neon_slide_hash(Posf *head, Posf *prev,
const unsigned short w_size,
const uInt hash_size)
{
/*
* SIMD implementation for hash table rebase assumes:
* 1. hash chain offset (Pos) is 2 bytes.
* 2. hash table size is multiple of 32 bytes.
* #1 should be true as Pos is defined as "ush"
* #2 should be true as hash_bits are greater than 7
*/
const size_t size = hash_size * sizeof(head[0]);
Assert(sizeof(Pos) == 2, "Wrong Pos size.");
Assert((size % sizeof(uint16x8_t) * 2) == 0, "Hash table size error.");
neon_slide_hash_update(head, hash_size, w_size);
#ifndef FASTEST
neon_slide_hash_update(prev, w_size, w_size);
#endif
}
#endif

4
deps/zlib/contrib/tests/DEPS vendored Normal file
View File

@ -0,0 +1,4 @@
include_rules = [
"+testing/gtest",
"+base",
]

View File

@ -1 +1,2 @@
cblume@chromium.org
cavalcantii@chromium.org

View File

@ -1,4 +1,4 @@
# Copyright 2017 The Chromium Authors. All rights reserved.
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@ -9,37 +9,32 @@ 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_inflate_with_header_fuzzer") {
sources = [ "inflate_with_header_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" ]
}

View File

@ -1,2 +1,3 @@
cblume@chromium.org
mmoroz@chromium.org
hans@chromium.org
noel@chromium.org

View File

@ -1,47 +1,74 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuzzer/FuzzedDataProvider.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <vector>
#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<unsigned char> input_buffer{data, data+size};
uLongf buffer_length = static_cast<uLongf>(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<uint8_t> src = fdp.ConsumeRemainingBytes<uint8_t>();
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<uint8_t> 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<uint8_t> 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;
}

View File

@ -1,4 +1,4 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

View File

@ -0,0 +1,95 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <memory>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fuzzer/FuzzedDataProvider.h>
#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)
static void chunked_inflate(gz_header* header,
uint8_t* data,
size_t size,
size_t in_chunk_size,
size_t out_chunk_size) {
z_stream stream;
stream.next_in = data;
stream.avail_in = 0;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
static const int kDefaultWindowBits = MAX_WBITS;
static const int kGzipOrZlibHeader = 32;
ASSERT(inflateInit2(&stream, kDefaultWindowBits + kGzipOrZlibHeader) == Z_OK);
ASSERT(inflateGetHeader(&stream, header) == Z_OK);
auto out_buffer = std::make_unique<uint8_t[]>(out_chunk_size);
while (true) {
stream.next_in = &data[stream.total_in];
stream.avail_in =
std::min(in_chunk_size, size - static_cast<size_t>(stream.total_in));
stream.next_out = out_buffer.get();
stream.avail_out = out_chunk_size;
if (inflate(&stream, stream.avail_in == 0 ? Z_SYNC_FLUSH : Z_NO_FLUSH) !=
Z_OK) {
break;
}
}
inflateEnd(&stream);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size > 250 * 1024) {
// Cap the input size so the fuzzer doesn't time out. For example,
// crbug.com/1362206 saw timeouts with 450 KB input, so use a limit that's
// well below that but still large enough to hit most code.
return 0;
}
FuzzedDataProvider fdp(data, size);
// Fuzz zlib's inflate() with inflateGetHeader() enabled, various sizes for
// the gz_header field sizes, and various-sized chunks for input/output. This
// would have found CVE-2022-37434 which was a heap buffer read overflow when
// filling in gz_header's extra field.
gz_header header;
header.extra_max = fdp.ConsumeIntegralInRange(0, 100000);
header.name_max = fdp.ConsumeIntegralInRange(0, 100000);
header.comm_max = fdp.ConsumeIntegralInRange(0, 100000);
auto extra_buf = std::make_unique<uint8_t[]>(header.extra_max);
auto name_buf = std::make_unique<uint8_t[]>(header.name_max);
auto comment_buf = std::make_unique<uint8_t[]>(header.comm_max);
header.extra = extra_buf.get();
header.name = name_buf.get();
header.comment = comment_buf.get();
size_t in_chunk_size = fdp.ConsumeIntegralInRange(1, 4097);
size_t out_chunk_size = fdp.ConsumeIntegralInRange(1, 4097);
std::vector<uint8_t> remaining_data = fdp.ConsumeRemainingBytes<uint8_t>();
chunked_inflate(&header, remaining_data.data(), remaining_data.size(),
in_chunk_size, out_chunk_size);
return 0;
}

View File

@ -0,0 +1,74 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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;
}

View File

@ -1,4 +1,4 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

746
deps/zlib/contrib/tests/infcover.cc vendored Normal file
View File

@ -0,0 +1,746 @@
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <iostream>
#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<struct mem_zone *>(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<struct mem_item *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<unsigned char *>(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<unsigned char *>(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<unsigned char *>(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(&copy, &strm); assert(ret == Z_OK);
ret = inflateEnd(&copy); 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(&copy, &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<struct inflate_state *>(((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<unsigned char *>(malloc(size));
assert(out != NULL);
win = static_cast<unsigned char *>(malloc(32768));
assert(win != NULL);
prefix = static_cast<char *>(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);
}
/* Adapted from Evgeny Legerov PoC (https://github.com/ivd38/zlib_overflow)
* this test case crashes in ASAN builds with the correct payload.
*/
local void inf_cve_2022_37434(char *hex, 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<unsigned char *>(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);
if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_NEED_DICT)
break;
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);
ret = inflateEnd(&strm);
mem_done(&strm, what);
}
void cover_CVE_2022_37434(void)
{
char payload[] = "1f 8b 08 04 61 62 63 64 61 62 52 51 1f 8b 08 04 61 62 63 64 61 62 52 51 1f 8b 08 04 61 62 63 64 61 62 52 51 1f 8b 08 04 61 62 63 64 61 62 52 51";
char cve[] = "wtf";
inf_cve_2022_37434(payload, cve, 13, 47, 12, Z_OK);
}
// clang-format on

12
deps/zlib/contrib/tests/infcover.h vendored Normal file
View File

@ -0,0 +1,12 @@
#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);
void cover_CVE_2022_37434(void);
#endif

View File

@ -0,0 +1,14 @@
// Copyright 2020 The Chromium Authors
// 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)));
}

1017
deps/zlib/contrib/tests/utils_unittest.cc vendored Normal file

File diff suppressed because it is too large Load Diff

170
deps/zlib/cpu_features.c vendored Normal file
View File

@ -0,0 +1,170 @@
/* cpu_features.c -- Processor features detection.
*
* Copyright 2018 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
#include "cpu_features.h"
#include "zutil.h"
#include <stdint.h>
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(ADLER32_SIMD_SSSE3)
#include <cpuid.h>
#endif
/* TODO(cavalcantii): remove checks for x86_flags on deflate.
*/
#if defined(ARMV8_OS_MACOS)
/* Crypto extensions (crc32/pmull) are a baseline feature in ARMv8.1-A, and
* OSX running on arm64 is new enough that these can be assumed without
* runtime detection.
*/
int ZLIB_INTERNAL arm_cpu_enable_crc32 = 1;
int ZLIB_INTERNAL arm_cpu_enable_pmull = 1;
#else
int ZLIB_INTERNAL arm_cpu_enable_crc32 = 0;
int ZLIB_INTERNAL arm_cpu_enable_pmull = 0;
#endif
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 <pthread.h>
#endif
#if defined(ARMV8_OS_ANDROID)
#include <cpu-features.h>
#elif defined(ARMV8_OS_LINUX)
#include <asm/hwcap.h>
#include <sys/auxv.h>
#elif defined(ARMV8_OS_FUCHSIA)
#include <zircon/features.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS)
#include <windows.h>
#elif !defined(_MSC_VER)
#include <pthread.h>
#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

18
deps/zlib/cpu_features.h vendored Normal file
View File

@ -0,0 +1,18 @@
/* cpu_features.h -- Processor features detection.
*
* Copyright 2018 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
#include "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 cpu_check_features(void);

1371
deps/zlib/crc32.c vendored

File diff suppressed because it is too large Load Diff

9877
deps/zlib/crc32.h vendored

File diff suppressed because it is too large Load Diff

206
deps/zlib/crc32_simd.c vendored
View File

@ -1,6 +1,6 @@
/* crc32_simd.c
*
* Copyright 2017 The Chromium Authors. All rights reserved.
* Copyright 2017 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
@ -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 <smmintrin.h>
#include <wmmintrin.h>
__attribute__((target("sse4.2,pclmul")))
uint32_t ZLIB_INTERNAL crc32_sse42_simd_( /* SSE4.2+PCLMUL */
const unsigned char *buf,
z_size_t len,
@ -161,11 +157,16 @@ uint32_t ZLIB_INTERNAL crc32_sse42_simd_( /* SSE4.2+PCLMUL */
#elif defined(CRC32_ARMV8_CRC32)
/* CRC32 checksums using ARMv8-a crypto instructions.
*
* TODO: implement a version using the PMULL instruction.
*/
#if defined(__clang__)
/* We need some extra types for using PMULL.
*/
#if defined(__aarch64__)
#include <arm_neon.h>
#include <arm_acle.h>
#endif
/* CRC32 intrinsics are #ifdef'ed out of arm_acle.h unless we build with an
* armv8 target, which is incompatible with ThinLTO optimizations on Android.
* (Namely, mixing and matching different module-level targets makes ThinLTO
@ -181,14 +182,21 @@ uint32_t ZLIB_INTERNAL crc32_sse42_simd_( /* SSE4.2+PCLMUL */
* NOTE: clang currently complains that "'+soft-float-abi' is not a recognized
* feature for this target (ignoring feature)." This appears to be a harmless
* bug in clang.
*
* These definitions must appear *after* including arm_acle.h otherwise that
* header may end up defining functions named __builtin_arm_crc32* that call
* themselves, creating an infinite loop when the intrinsic is called.
*/
/* XXX: Cannot hook into builtins with XCode for arm64. */
#if !defined(ARMV8_OS_MACOS)
#define __crc32b __builtin_arm_crc32b
#define __crc32d __builtin_arm_crc32d
#define __crc32w __builtin_arm_crc32w
#define __crc32cw __builtin_arm_crc32cw
#endif
#if defined(__aarch64__)
#define TARGET_ARMV8_WITH_CRC __attribute__((target("crc")))
#define TARGET_ARMV8_WITH_CRC __attribute__((target("aes,crc")))
#else // !defined(__aarch64__)
#define TARGET_ARMV8_WITH_CRC __attribute__((target("armv8-a,crc")))
#endif // defined(__aarch64__)
@ -198,15 +206,17 @@ uint32_t ZLIB_INTERNAL crc32_sse42_simd_( /* SSE4.2+PCLMUL */
* allowed. We can just include arm_acle.h.
*/
#include <arm_acle.h>
#include <arm_neon.h>
#define TARGET_ARMV8_WITH_CRC
#else // !defined(__GNUC__) && !defined(_aarch64__)
#error ARM CRC32 SIMD extensions only supported for Clang and GCC
#endif
TARGET_ARMV8_WITH_CRC
uint32_t ZLIB_INTERNAL armv8_crc32_little(unsigned long crc,
const unsigned char *buf,
z_size_t len)
uint32_t ZLIB_INTERNAL armv8_crc32_little(
const unsigned char *buf,
z_size_t len,
uint32_t crc)
{
uint32_t c = (uint32_t) ~crc;
@ -244,4 +254,178 @@ uint32_t ZLIB_INTERNAL armv8_crc32_little(unsigned long crc,
return ~c;
}
#if defined(__aarch64__) || defined(ARMV8_OS_MACOS) /* aarch64 specific code. */
/*
* crc32_pmull_simd_(): compute the crc32 of the buffer, where the buffer
* length must be at least 64, and a multiple of 16. Based on:
*
* "Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction"
* V. Gopal, E. Ozturk, et al., 2009, http://intel.ly/2ySEwL0
*/
TARGET_ARMV8_WITH_CRC
static inline uint8x16_t pmull_lo(const uint64x2_t a, const uint64x2_t b)
{
uint8x16_t r;
__asm__ __volatile__ ("pmull %0.1q, %1.1d, %2.1d \n\t"
: "=w" (r) : "w" (a), "w" (b) );
return r;
}
TARGET_ARMV8_WITH_CRC
static inline uint8x16_t pmull_01(const uint64x2_t a, const uint64x2_t b)
{
uint8x16_t r;
__asm__ __volatile__ ("pmull %0.1q, %1.1d, %2.1d \n\t"
: "=w" (r) : "w" (a), "w" (vgetq_lane_u64(b, 1)) );
return r;
}
TARGET_ARMV8_WITH_CRC
static inline uint8x16_t pmull_hi(const uint64x2_t a, const uint64x2_t b)
{
uint8x16_t r;
__asm__ __volatile__ ("pmull2 %0.1q, %1.2d, %2.2d \n\t"
: "=w" (r) : "w" (a), "w" (b) );
return r;
}
TARGET_ARMV8_WITH_CRC
uint32_t ZLIB_INTERNAL armv8_crc32_pmull_little(
const unsigned char *buf,
z_size_t len,
uint32_t crc)
{
/*
* Definitions of the bit-reflected domain constants k1,k2,k3, etc and
* the CRC32+Barrett polynomials given at the end of the paper.
*/
static const uint64_t zalign(16) k1k2[] = { 0x0154442bd4, 0x01c6e41596 };
static const uint64_t zalign(16) k3k4[] = { 0x01751997d0, 0x00ccaa009e };
static const uint64_t zalign(16) k5k0[] = { 0x0163cd6124, 0x0000000000 };
static const uint64_t zalign(16) poly[] = { 0x01db710641, 0x01f7011641 };
uint64x2_t x0, x1, x2, x3, x4, x5, x6, x7, x8, y5, y6, y7, y8;
/*
* There's at least one block of 64.
*/
x1 = vld1q_u64((const uint64_t *)(buf + 0x00));
x2 = vld1q_u64((const uint64_t *)(buf + 0x10));
x3 = vld1q_u64((const uint64_t *)(buf + 0x20));
x4 = vld1q_u64((const uint64_t *)(buf + 0x30));
x1 = veorq_u64(x1, (uint64x2_t) vsetq_lane_u32(crc, vdupq_n_u32(0), 0));
x0 = vld1q_u64(k1k2);
buf += 64;
len -= 64;
/*
* Parallel fold blocks of 64, if any.
*/
while (len >= 64)
{
x5 = (uint64x2_t) pmull_lo(x1, x0);
x6 = (uint64x2_t) pmull_lo(x2, x0);
x7 = (uint64x2_t) pmull_lo(x3, x0);
x8 = (uint64x2_t) pmull_lo(x4, x0);
y5 = vld1q_u64((const uint64_t *)(buf + 0x00));
y6 = vld1q_u64((const uint64_t *)(buf + 0x10));
y7 = vld1q_u64((const uint64_t *)(buf + 0x20));
y8 = vld1q_u64((const uint64_t *)(buf + 0x30));
x1 = (uint64x2_t) pmull_hi(x1, x0);
x2 = (uint64x2_t) pmull_hi(x2, x0);
x3 = (uint64x2_t) pmull_hi(x3, x0);
x4 = (uint64x2_t) pmull_hi(x4, x0);
x1 = veorq_u64(x1, x5);
x2 = veorq_u64(x2, x6);
x3 = veorq_u64(x3, x7);
x4 = veorq_u64(x4, x8);
x1 = veorq_u64(x1, y5);
x2 = veorq_u64(x2, y6);
x3 = veorq_u64(x3, y7);
x4 = veorq_u64(x4, y8);
buf += 64;
len -= 64;
}
/*
* Fold into 128-bits.
*/
x0 = vld1q_u64(k3k4);
x5 = (uint64x2_t) pmull_lo(x1, x0);
x1 = (uint64x2_t) pmull_hi(x1, x0);
x1 = veorq_u64(x1, x2);
x1 = veorq_u64(x1, x5);
x5 = (uint64x2_t) pmull_lo(x1, x0);
x1 = (uint64x2_t) pmull_hi(x1, x0);
x1 = veorq_u64(x1, x3);
x1 = veorq_u64(x1, x5);
x5 = (uint64x2_t) pmull_lo(x1, x0);
x1 = (uint64x2_t) pmull_hi(x1, x0);
x1 = veorq_u64(x1, x4);
x1 = veorq_u64(x1, x5);
/*
* Single fold blocks of 16, if any.
*/
while (len >= 16)
{
x2 = vld1q_u64((const uint64_t *)buf);
x5 = (uint64x2_t) pmull_lo(x1, x0);
x1 = (uint64x2_t) pmull_hi(x1, x0);
x1 = veorq_u64(x1, x2);
x1 = veorq_u64(x1, x5);
buf += 16;
len -= 16;
}
/*
* Fold 128-bits to 64-bits.
*/
static uint32_t zalign(16) mask[] = { ~0u, 0u, ~0u, 0u };
x2 = (uint64x2_t) pmull_01(x1, x0);
x1 = (uint64x2_t) vextq_u8(vreinterpretq_u8_u64(x1), vdupq_n_u8(0), 8);
x3 = (uint64x2_t) vld1q_u32(mask);
x1 = veorq_u64(x1, x2);
x0 = vld1q_u64(k5k0);
x2 = (uint64x2_t) pmull_01(x2, x0);
x2 = (uint64x2_t) vextq_u8(vreinterpretq_u8_u64(x1), vdupq_n_u8(0), 4);
x1 = vandq_u64(x1, x3);
x1 = (uint64x2_t) pmull_lo(x1, x0);
x1 = veorq_u64(x1, x2);
/*
* Barret reduce to 32-bits.
*/
x0 = vld1q_u64(poly);
x2 = vandq_u64(x1, x3);
x2 = (uint64x2_t) pmull_01(x2, x0);
x2 = vandq_u64(x2, x3);
x2 = (uint64x2_t) pmull_lo(x2, x0);
x1 = veorq_u64(x1, x2);
/*
* Return the crc32.
*/
return vgetq_lane_u32(vreinterpretq_u32_u64(x1), 1);
}
#endif /* aarch64 specific code. */
#endif

View File

@ -1,6 +1,6 @@
/* crc32_simd.h
*
* Copyright 2017 The Chromium Authors. All rights reserved.
* Copyright 2017 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
@ -15,10 +15,9 @@
* crc32_sse42_simd_(): compute the crc32 of the buffer, where the buffer
* length must be at least 64, and a multiple of 16.
*/
uint32_t ZLIB_INTERNAL crc32_sse42_simd_(
const unsigned char *buf,
z_size_t len,
uint32_t crc);
uint32_t ZLIB_INTERNAL crc32_sse42_simd_(const unsigned char* buf,
z_size_t len,
uint32_t crc);
/*
* crc32_sse42_simd_ buffer size constraints: see the use in zlib/crc32.c
@ -30,7 +29,23 @@ uint32_t ZLIB_INTERNAL crc32_sse42_simd_(
/*
* CRC32 checksums using ARMv8-a crypto instructions.
*/
uint32_t ZLIB_INTERNAL armv8_crc32_little(unsigned long crc,
const unsigned char* buf,
z_size_t len);
uint32_t ZLIB_INTERNAL armv8_crc32_little(const unsigned char* buf,
z_size_t len,
uint32_t crc);
/* aarch64 specific code. */
#if defined(__aarch64__)
/* 128 is the sweet spot at the time of coding (late 2020). */
#define Z_CRC32_PMULL_MINIMUM_LENGTH 128
#define Z_CRC32_PMULL_CHUNKSIZE_MASK 15
/*
* CRC32 checksums using ARMv8-a PMULL instructions, where the buffer
* length must be at least 64, and a multiple of 16.
*/
uint32_t ZLIB_INTERNAL armv8_crc32_pmull_little(const unsigned char* buf,
z_size_t len,
uint32_t crc);
#endif

View File

@ -18,15 +18,13 @@
#include "deflate.h"
#ifdef CRC32_SIMD_SSE42_PCLMUL
#include <inttypes.h>
#include <emmintrin.h>
#include <immintrin.h>
#include <wmmintrin.h>
#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 */

379
deps/zlib/deflate.c vendored
View File

@ -1,5 +1,5 @@
/* deflate.c -- compress data using the deflation algorithm
* Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
* Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -50,18 +50,22 @@
/* @(#) $Id$ */
#include <assert.h>
#include "deflate.h"
#include "x86.h"
#include "cpu_features.h"
#if defined(DEFLATE_SLIDE_HASH_SSE2) || defined(DEFLATE_SLIDE_HASH_NEON)
#include "slide_hash_simd.h"
#endif
#include "contrib/optimizations/insert_string.h"
#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
#include "contrib/optimizations/slide_hash_neon.h"
#endif
#if defined(CRC32_ARMV8_CRC32)
#include "crc32_simd.h"
#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 ";
" deflate 1.2.13 Copyright 1995-2022 Jean-loup Gailly and Mark Adler ";
/*
If you use the zlib library in a product, an acknowledgment is welcome
in the documentation of your product. If for some reason you cannot
@ -95,14 +99,8 @@ local block_state deflate_huff OF((deflate_state *s, int flush));
local void lm_init OF((deflate_state *s));
local void putShortMSB OF((deflate_state *s, uInt b));
local void flush_pending OF((z_streamp strm));
unsigned ZLIB_INTERNAL deflate_read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
#ifdef ASMV
# pragma message("Assembler code may have bugs -- use at your own risk")
void match_init OF((void)); /* asm code initialization */
uInt longest_match OF((deflate_state *s, IPos cur_match));
#else
local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
local uInt longest_match OF((deflate_state *s, IPos cur_match));
#endif
#ifdef ZLIB_DEBUG
local void check_match OF((deflate_state *s, IPos start, IPos match,
@ -171,10 +169,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
@ -184,10 +187,11 @@ local const config configuration_table[10] = {
local void slide_hash(s)
deflate_state *s;
{
#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
/* NEON based hash table rebase. */
return neon_slide_hash(s->head, s->prev, s->w_size, s->hash_size);
#if defined(DEFLATE_SLIDE_HASH_SSE2) || defined(DEFLATE_SLIDE_HASH_NEON)
slide_hash_simd(s->head, s->prev, s->w_size, s->hash_size);
return;
#endif
unsigned n, m;
Posf *p;
uInt wsize = s->w_size;
@ -244,10 +248,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] ||
@ -280,6 +282,8 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
if (windowBits < 0) { /* suppress zlib wrapper */
wrap = 0;
if (windowBits < -15)
return Z_STREAM_ERROR;
windowBits = -windowBits;
}
#ifdef GZIP
@ -306,20 +310,37 @@ 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->chromium_zlib_hash = 0;
#if !defined(USE_ZLIB_RABIN_KARP_ROLLING_HASH)
#if defined(TARGET_CPU_WITH_CRC) && defined(CRC32_SIMD_SSE42_PCLMUL)
if (x86_cpu_enable_simd)
s->chromium_zlib_hash = 1;
#elif defined(TARGET_CPU_WITH_CRC) && defined(CRC32_ARMV8_CRC32)
if (arm_cpu_enable_crc32)
s->chromium_zlib_hash = 1;
#endif
#endif
s->hash_bits = memLevel + 7;
if (s->chromium_zlib_hash && s->hash_bits < 15) {
s->hash_bits = 15;
} else {
s->hash_bits = memLevel + 7;
}
s->hash_size = 1 << s->hash_bits;
s->hash_mask = s->hash_size - 1;
s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
s->hash_shift = ((s->hash_bits + MIN_MATCH-1) / MIN_MATCH);
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 */
@ -342,11 +363,11 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
* sym_buf value to read moves forward three bytes. From that symbol, up to
* 31 bits are written to pending_buf. The closest the written pending_buf
* bits gets to the next sym_buf symbol to read is just before the last
* code is written. At that time, 31*(n-2) bits have been written, just
* after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at
* 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1
* code is written. At that time, 31*(n - 2) bits have been written, just
* after 24*(n - 2) bits have been consumed from sym_buf. sym_buf starts at
* 8*n bits into pending_buf. (Note that the symbol buffer fills when n - 1
* symbols are written.) The closest the writing gets to what is unread is
* then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and
* then n + 14 bits. Here n is lit_bufsize, which is 16384 by default, and
* can range from 128 to 32768.
*
* Therefore, at a minimum, there are 142 bits of space between what is
@ -391,7 +412,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
/* =========================================================================
* Check for a valid deflate stream state. Return 0 if ok, 1 if not.
*/
local int deflateStateCheck (strm)
local int deflateStateCheck(strm)
z_streamp strm;
{
deflate_state *s;
@ -414,7 +435,7 @@ local int deflateStateCheck (strm)
}
/* ========================================================================= */
int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
int ZEXPORT deflateSetDictionary(strm, dictionary, dictLength)
z_streamp strm;
const Bytef *dictionary;
uInt dictLength;
@ -435,7 +456,7 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
/* when using zlib wrappers, compute Adler-32 for provided dictionary */
if (wrap == 1)
strm->adler = adler32(strm->adler, dictionary, dictLength);
s->wrap = 0; /* avoid computing Adler-32 in deflate_read_buf */
s->wrap = 0; /* avoid computing Adler-32 in read_buf */
/* if dictionary would fill window, just replace the history */
if (dictLength >= s->w_size) {
@ -479,7 +500,7 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
}
/* ========================================================================= */
int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)
int ZEXPORT deflateGetDictionary(strm, dictionary, dictLength)
z_streamp strm;
Bytef *dictionary;
uInt *dictLength;
@ -501,7 +522,7 @@ int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)
}
/* ========================================================================= */
int ZEXPORT deflateResetKeep (strm)
int ZEXPORT deflateResetKeep(strm)
z_streamp strm;
{
deflate_state *s;
@ -525,13 +546,13 @@ int ZEXPORT deflateResetKeep (strm)
#ifdef GZIP
s->wrap == 2 ? GZIP_STATE :
#endif
s->wrap ? INIT_STATE : BUSY_STATE;
INIT_STATE;
strm->adler =
#ifdef GZIP
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);
@ -539,7 +560,7 @@ int ZEXPORT deflateResetKeep (strm)
}
/* ========================================================================= */
int ZEXPORT deflateReset (strm)
int ZEXPORT deflateReset(strm)
z_streamp strm;
{
int ret;
@ -551,7 +572,7 @@ int ZEXPORT deflateReset (strm)
}
/* ========================================================================= */
int ZEXPORT deflateSetHeader (strm, head)
int ZEXPORT deflateSetHeader(strm, head)
z_streamp strm;
gz_headerp head;
{
@ -562,7 +583,7 @@ int ZEXPORT deflateSetHeader (strm, head)
}
/* ========================================================================= */
int ZEXPORT deflatePending (strm, pending, bits)
int ZEXPORT deflatePending(strm, pending, bits)
unsigned *pending;
int *bits;
z_streamp strm;
@ -576,7 +597,7 @@ int ZEXPORT deflatePending (strm, pending, bits)
}
/* ========================================================================= */
int ZEXPORT deflatePrime (strm, bits, value)
int ZEXPORT deflatePrime(strm, bits, value)
z_streamp strm;
int bits;
int value;
@ -586,7 +607,8 @@ int ZEXPORT deflatePrime (strm, bits, value)
if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
s = strm->state;
if (s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
if (bits < 0 || bits > 16 ||
s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
return Z_BUF_ERROR;
do {
put = Buf_size - s->bi_valid;
@ -624,12 +646,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) {
@ -670,36 +692,50 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
}
/* =========================================================================
* For the default windowBits of 15 and memLevel of 8, this function returns
* a close to exact, as well as small, upper bound on the compressed size.
* They are coded as constants here for a reason--if the #define's are
* changed, then this function needs to be changed as well. The return
* value for 15 and 8 only works for those exact settings.
* For the default windowBits of 15 and memLevel of 8, this function returns a
* close to exact, as well as small, upper bound on the compressed size. This
* is an expansion of ~0.03%, plus a small constant.
*
* For any setting other than those defaults for windowBits and memLevel,
* the value returned is a conservative worst case for the maximum expansion
* resulting from using fixed blocks instead of stored blocks, which deflate
* can emit on compressed data for some combinations of the parameters.
* For any setting other than those defaults for windowBits and memLevel, one
* of two worst case bounds is returned. This is at most an expansion of ~4% or
* ~13%, plus a small constant.
*
* This function could be more sophisticated to provide closer upper bounds for
* every combination of windowBits and memLevel. But even the conservative
* upper bound of about 14% expansion does not seem onerous for output buffer
* allocation.
* Both the 0.03% and 4% derive from the overhead of stored blocks. The first
* one is for stored blocks of 16383 bytes (memLevel == 8), whereas the second
* is for stored blocks of 127 bytes (the worst case memLevel == 1). The
* expansion results from five bytes of header for each stored block.
*
* The larger expansion of 13% results from a window size less than or equal to
* the symbols buffer size (windowBits <= memLevel + 7). In that case some of
* the data being compressed may have slid out of the sliding window, impeding
* a stored block from being emitted. Then the only choice is a fixed or
* dynamic block, where a fixed block limits the maximum expansion to 9 bits
* per 8-bit byte, plus 10 bits for every block. The smallest block size for
* which this can occur is 255 (memLevel == 2).
*
* Shifts are used to approximate divisions, for speed.
*/
uLong ZEXPORT deflateBound(strm, sourceLen)
z_streamp strm;
uLong sourceLen;
{
deflate_state *s;
uLong complen, wraplen;
uLong fixedlen, storelen, wraplen;
/* conservative upper bound for compressed data */
complen = sourceLen +
((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
/* upper bound for fixed blocks with 9-bit literals and length 255
(memLevel == 2, which is the lowest that may not use stored blocks) --
~13% overhead plus a small constant */
fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) +
(sourceLen >> 9) + 4;
/* if can't get parameters, return conservative bound plus zlib wrapper */
/* upper bound for stored blocks with length 127 (memLevel == 1) --
~4% overhead plus a small constant */
storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) +
(sourceLen >> 11) + 7;
/* if can't get parameters, return larger bound plus a zlib wrapper */
if (deflateStateCheck(strm))
return complen + 6;
return (fixedlen > storelen ? fixedlen : storelen) + 6;
/* compute wrapper length */
s = strm->state;
@ -736,11 +772,12 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
wraplen = 6;
}
/* if not default parameters, return conservative bound */
/* if not default parameters, return one of the conservative bounds */
if (s->w_bits != 15 || s->hash_bits != 8 + 7)
return complen + wraplen;
return (s->w_bits <= s->hash_bits ? fixedlen : storelen) + wraplen;
/* default settings: return tight bound for that case */
/* default settings: return tight bound for that case -- ~0.03% overhead
plus a small constant */
return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
(sourceLen >> 25) + 13 - 6 + wraplen;
}
@ -750,7 +787,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
* IN assertion: the stream state is correct and there is enough room in
* pending_buf.
*/
local void putShortMSB (s, b)
local void putShortMSB(s, b)
deflate_state *s;
uInt b;
{
@ -762,7 +799,7 @@ local void putShortMSB (s, b)
* Flush as much pending output as possible. All deflate() output, except for
* some deflate_stored() output, goes through this function so some
* applications may wish to modify it to avoid allocating a large
* strm->next_out buffer and copying into it. (See also deflate_read_buf()).
* strm->next_out buffer and copying into it. (See also read_buf()).
*/
local void flush_pending(strm)
z_streamp strm;
@ -797,7 +834,7 @@ local void flush_pending(strm)
} while (0)
/* ========================================================================= */
int ZEXPORT deflate (strm, flush)
int ZEXPORT deflate(strm, flush)
z_streamp strm;
int flush;
{
@ -848,9 +885,11 @@ int ZEXPORT deflate (strm, flush)
}
/* Write the header */
if (s->status == INIT_STATE && s->wrap == 0)
s->status = BUSY_STATE;
if (s->status == INIT_STATE) {
/* zlib header */
uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
uInt header = (Z_DEFLATED + ((s->w_bits - 8) << 4)) << 8;
uInt level_flags;
if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
@ -1111,7 +1150,7 @@ int ZEXPORT deflate (strm, flush)
}
/* ========================================================================= */
int ZEXPORT deflateEnd (strm)
int ZEXPORT deflateEnd(strm)
z_streamp strm;
{
int status;
@ -1137,7 +1176,7 @@ int ZEXPORT deflateEnd (strm)
* To simplify the source, this is not supported for 16-bit MSDOS (which
* doesn't have enough memory anyway to duplicate compression states).
*/
int ZEXPORT deflateCopy (dest, source)
int ZEXPORT deflateCopy(dest, source)
z_streamp dest;
z_streamp source;
{
@ -1196,7 +1235,7 @@ int ZEXPORT deflateCopy (dest, source)
* allocating a large strm->next_in buffer and copying from it.
* (See also flush_pending()).
*/
ZLIB_INTERNAL unsigned deflate_read_buf(strm, buf, size)
local unsigned read_buf(strm, buf, size)
z_streamp strm;
Bytef *buf;
unsigned size;
@ -1211,7 +1250,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);
@ -1227,7 +1266,7 @@ ZLIB_INTERNAL unsigned deflate_read_buf(strm, buf, size)
/* ===========================================================================
* Initialize the "longest match" routines for a new zlib stream
*/
local void lm_init (s)
local void lm_init(s)
deflate_state *s;
{
s->window_size = (ulg)2L*s->w_size;
@ -1248,11 +1287,6 @@ local void lm_init (s)
s->match_length = s->prev_length = MIN_MATCH-1;
s->match_available = 0;
s->ins_h = 0;
#ifndef FASTEST
#ifdef ASMV
match_init(); /* initialize the asm code */
#endif
#endif
}
#ifndef FASTEST
@ -1265,10 +1299,6 @@ local void lm_init (s)
* string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
* OUT assertion: the match length is not greater than s->lookahead.
*/
#ifndef ASMV
/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
* match.S. The code will be functionally equivalent.
*/
local uInt longest_match(s, cur_match)
deflate_state *s;
IPos cur_match; /* current match */
@ -1293,10 +1323,10 @@ local uInt longest_match(s, cur_match)
*/
register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
register ush scan_start = *(ushf*)scan;
register ush scan_end = *(ushf*)(scan+best_len-1);
register ush scan_end = *(ushf*)(scan + best_len - 1);
#else
register Bytef *strend = s->window + s->strstart + MAX_MATCH;
register Byte scan_end1 = scan[best_len-1];
register Byte scan_end1 = scan[best_len - 1];
register Byte scan_end = scan[best_len];
#endif
@ -1314,7 +1344,8 @@ local uInt longest_match(s, cur_match)
*/
if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead;
Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
"need lookahead");
do {
Assert(cur_match < s->strstart, "no future");
@ -1332,53 +1363,72 @@ local uInt longest_match(s, cur_match)
/* This code assumes sizeof(unsigned short) == 2. Do not use
* UNALIGNED_OK if your compiler uses a different size.
*/
if (*(ushf*)(match+best_len-1) != scan_end ||
if (*(ushf*)(match + best_len - 1) != scan_end ||
*(ushf*)match != scan_start) continue;
/* It is not necessary to compare scan[2] and match[2] since they are
* always equal when the other bytes match, given that the hash keys
* are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
* strstart+3, +5, ... up to strstart+257. We check for insufficient
* strstart + 3, + 5, up to strstart + 257. We check for insufficient
* lookahead only every 4th comparison; the 128th check will be made
* at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
* at strstart + 257. If MAX_MATCH-2 is not a multiple of 8, it is
* 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 (!s->chromium_zlib_hash) {
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) &&
*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
} while (*(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
*(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
*(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
*(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
scan < strend);
/* The funny "do {}" generates better code on most compilers */
/* Here, scan <= window+strstart+257 */
Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
/* Here, scan <= window + strstart + 257 */
Assert(scan <= s->window+(unsigned)(s->window_size - 1),
"wild scan");
if (*scan == *match) scan++;
len = (MAX_MATCH - 1) - (int)(strend-scan);
len = (MAX_MATCH - 1) - (int)(strend - scan);
scan = strend - (MAX_MATCH-1);
#else /* UNALIGNED_OK */
if (match[best_len] != scan_end ||
match[best_len-1] != scan_end1 ||
match[best_len - 1] != scan_end1 ||
*match != *scan ||
*++match != scan[1]) continue;
/* The check at best_len-1 can be removed because it will be made
/* The check at best_len - 1 can be removed because it will be made
* again later. (This heuristic is not always a win.)
* It is not necessary to compare scan[2] and match[2] since they
* are always equal when the other bytes match, given that
* the hash keys are equal and that HASH_BITS >= 8.
*/
scan += 2, match++;
Assert(*scan == *match, "match[2]?");
if (!s->chromium_zlib_hash) {
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.
* the 256th check will be made at strstart + 258.
*/
do {
} while (*++scan == *++match && *++scan == *++match &&
@ -1387,7 +1437,8 @@ local uInt longest_match(s, cur_match)
*++scan == *++match && *++scan == *++match &&
scan < strend);
Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
Assert(scan <= s->window + (unsigned)(s->window_size - 1),
"wild scan");
len = MAX_MATCH - (int)(strend - scan);
scan = strend - MAX_MATCH;
@ -1399,9 +1450,9 @@ local uInt longest_match(s, cur_match)
best_len = len;
if (len >= nice_match) break;
#ifdef UNALIGNED_OK
scan_end = *(ushf*)(scan+best_len-1);
scan_end = *(ushf*)(scan + best_len - 1);
#else
scan_end1 = scan[best_len-1];
scan_end1 = scan[best_len - 1];
scan_end = scan[best_len];
#endif
}
@ -1411,7 +1462,6 @@ local uInt longest_match(s, cur_match)
if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
return s->lookahead;
}
#endif /* ASMV */
#else /* FASTEST */
@ -1432,7 +1482,8 @@ local uInt longest_match(s, cur_match)
*/
Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
"need lookahead");
Assert(cur_match < s->strstart, "no future");
@ -1442,7 +1493,7 @@ local uInt longest_match(s, cur_match)
*/
if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
/* The check at best_len-1 can be removed because it will be made
/* The check at best_len - 1 can be removed because it will be made
* again later. (This heuristic is not always a win.)
* It is not necessary to compare scan[2] and match[2] since they
* are always equal when the other bytes match, given that
@ -1452,7 +1503,7 @@ local uInt longest_match(s, cur_match)
Assert(*scan == *match, "match[2]?");
/* We check for insufficient lookahead only every 8th comparison;
* the 256th check will be made at strstart+258.
* the 256th check will be made at strstart + 258.
*/
do {
} while (*++scan == *++match && *++scan == *++match &&
@ -1461,7 +1512,7 @@ local uInt longest_match(s, cur_match)
*++scan == *++match && *++scan == *++match &&
scan < strend);
Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
Assert(scan <= s->window + (unsigned)(s->window_size - 1), "wild scan");
len = MAX_MATCH - (int)(strend - scan);
@ -1497,7 +1548,7 @@ local void check_match(s, start, match, length)
z_error("invalid match");
}
if (z_verbose > 1) {
fprintf(stderr,"\\[%d,%d]", start-match, length);
fprintf(stderr,"\\[%d,%d]", start - match, length);
do { putc(s->window[start++], stderr); } while (--length != 0);
}
}
@ -1515,19 +1566,7 @@ local void check_match(s, start, match, length)
* performed for at least two bytes (required for the zip translate_eol
* option -- not supported here).
*/
local void fill_window_c(deflate_state *s);
local void fill_window(deflate_state *s)
{
if (x86_cpu_enable_simd) {
fill_window_sse(s);
return;
}
fill_window_c(s);
}
local void fill_window_c(s)
local void fill_window(s)
deflate_state *s;
{
unsigned n;
@ -1555,12 +1594,14 @@ local void fill_window_c(s)
/* If the window is almost full and there is insufficient lookahead,
* move the upper half to the lower one to make room in the upper half.
*/
if (s->strstart >= wsize+MAX_DIST(s)) {
if (s->strstart >= wsize + MAX_DIST(s)) {
zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more);
zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more);
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;
}
@ -1579,9 +1620,23 @@ local void fill_window_c(s)
*/
Assert(more >= 2, "more < 2");
n = deflate_read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
s->lookahead += n;
/* Initialize the hash value now that we have some input: */
if (s->chromium_zlib_hash) {
/* chromium hash reads 4 bytes */
if (s->lookahead + s->insert > MIN_MATCH) {
uInt str = s->strstart - s->insert;
while (s->insert) {
insert_string(s, str);
str++;
s->insert--;
if (s->lookahead + s->insert <= MIN_MATCH)
break;
}
}
} else
/* Initialize the hash value now that we have some input: */
if (s->lookahead + s->insert >= MIN_MATCH) {
uInt str = s->strstart - s->insert;
@ -1686,7 +1741,7 @@ local void fill_window_c(s)
*
* deflate_stored() is written to minimize the number of times an input byte is
* copied. It is most efficient with large input and output buffers, which
* maximizes the opportunites to have a single copy from next_in to next_out.
* maximizes the opportunities to have a single copy from next_in to next_out.
*/
local block_state deflate_stored(s, flush)
deflate_state *s;
@ -1768,7 +1823,7 @@ local block_state deflate_stored(s, flush)
* the check value.
*/
if (len) {
deflate_read_buf(s->strm, s->strm->next_out, len);
read_buf(s->strm, s->strm->next_out, len);
s->strm->next_out += len;
s->strm->avail_out -= len;
s->strm->total_out += len;
@ -1790,6 +1845,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 +1854,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 +1876,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 +1885,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);
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;
@ -1890,7 +1951,7 @@ local block_state deflate_fast(s, flush)
if (s->lookahead == 0) break; /* flush the current block */
}
/* Insert the string window[strstart .. strstart+2] in the
/* Insert the string window[strstart .. strstart + 2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = NIL;
@ -1937,19 +1998,22 @@ local block_state deflate_fast(s, flush)
{
s->strstart += s->match_length;
s->match_length = 0;
s->ins_h = s->window[s->strstart];
UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
if (!s->chromium_zlib_hash) {
s->ins_h = s->window[s->strstart];
UPDATE_HASH(s, s->ins_h, s->window[s->strstart + 1]);
#if MIN_MATCH != 3
Call UPDATE_HASH() MIN_MATCH-3 more times
Call UPDATE_HASH() MIN_MATCH-3 more times
#endif
/* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
* matter since it will be recomputed at next deflate call.
*/
/* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
* matter since it will be recomputed at next deflate call.
*/
}
}
} else {
/* No match, output a literal byte */
Tracevv((stderr,"%c", s->window[s->strstart]));
_tr_tally_lit (s, s->window[s->strstart], bflush);
_tr_tally_lit(s, s->window[s->strstart], bflush);
s->lookahead--;
s->strstart++;
}
@ -1993,7 +2057,7 @@ local block_state deflate_slow(s, flush)
if (s->lookahead == 0) break; /* flush the current block */
}
/* Insert the string window[strstart .. strstart+2] in the
/* Insert the string window[strstart .. strstart + 2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = NIL;
@ -2035,17 +2099,23 @@ 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,
_tr_tally_dist(s, s->strstart - 1 - s->prev_match,
s->prev_length - MIN_MATCH, bflush);
/* Insert in hash table all strings up to the end of the match.
* strstart-1 and strstart are already inserted. If there is not
* strstart - 1 and strstart are already inserted. If there is not
* enough lookahead, the last two strings are not inserted in
* the hash table.
*/
s->lookahead -= s->prev_length-1;
s->lookahead -= s->prev_length - 1;
s->prev_length -= 2;
do {
if (++s->strstart <= max_insert) {
@ -2063,8 +2133,8 @@ local block_state deflate_slow(s, flush)
* single literal. If there was a match but the current match
* is longer, truncate the previous match to a single literal.
*/
Tracevv((stderr,"%c", s->window[s->strstart-1]));
_tr_tally_lit(s, s->window[s->strstart-1], bflush);
Tracevv((stderr,"%c", s->window[s->strstart - 1]));
_tr_tally_lit(s, s->window[s->strstart - 1], bflush);
if (bflush) {
FLUSH_BLOCK_ONLY(s, 0);
}
@ -2082,8 +2152,8 @@ local block_state deflate_slow(s, flush)
}
Assert (flush != Z_NO_FLUSH, "no flush?");
if (s->match_available) {
Tracevv((stderr,"%c", s->window[s->strstart-1]));
_tr_tally_lit(s, s->window[s->strstart-1], bflush);
Tracevv((stderr,"%c", s->window[s->strstart - 1]));
_tr_tally_lit(s, s->window[s->strstart - 1], bflush);
s->match_available = 0;
}
s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
@ -2140,7 +2210,8 @@ local block_state deflate_rle(s, flush)
if (s->match_length > s->lookahead)
s->match_length = s->lookahead;
}
Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
Assert(scan <= s->window + (uInt)(s->window_size - 1),
"wild scan");
}
/* Emit match if have run of MIN_MATCH or longer, else emit literal */
@ -2155,7 +2226,7 @@ local block_state deflate_rle(s, flush)
} else {
/* No match, output a literal byte */
Tracevv((stderr,"%c", s->window[s->strstart]));
_tr_tally_lit (s, s->window[s->strstart], bflush);
_tr_tally_lit(s, s->window[s->strstart], bflush);
s->lookahead--;
s->strstart++;
}
@ -2195,7 +2266,7 @@ local block_state deflate_huff(s, flush)
/* Output a literal byte */
s->match_length = 0;
Tracevv((stderr,"%c", s->window[s->strstart]));
_tr_tally_lit (s, s->window[s->strstart], bflush);
_tr_tally_lit(s, s->window[s->strstart], bflush);
s->lookahead--;
s->strstart++;
if (bflush) FLUSH_BLOCK(s, 0);

13
deps/zlib/deflate.h vendored
View File

@ -1,5 +1,5 @@
/* deflate.h -- internal compression state
* Copyright (C) 1995-2016 Jean-loup Gailly
* Copyright (C) 1995-2018 Jean-loup Gailly
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -268,6 +268,11 @@ typedef struct internal_state {
* updated to the new high water mark.
*/
uInt chromium_zlib_hash;
/* 0 if Rabin-Karp rolling hash is enabled, non-zero if chromium zlib
* hash is enabled.
*/
} FAR deflate_state;
/* Output a byte on the stream.
@ -329,8 +334,8 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
# define _tr_tally_dist(s, distance, length, flush) \
{ uch len = (uch)(length); \
ush dist = (ush)(distance); \
s->sym_buf[s->sym_next++] = dist; \
s->sym_buf[s->sym_next++] = dist >> 8; \
s->sym_buf[s->sym_next++] = (uch)dist; \
s->sym_buf[s->sym_next++] = (uch)(dist >> 8); \
s->sym_buf[s->sym_next++] = len; \
dist--; \
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
@ -351,6 +356,4 @@ void ZLIB_INTERNAL crc_fold_copy(deflate_state* const s,
long len);
unsigned ZLIB_INTERNAL crc_fold_512to32(deflate_state* const s);
void ZLIB_INTERNAL fill_window_sse(deflate_state* s);
#endif /* DEFLATE_H */

View File

@ -1,178 +0,0 @@
/*
* Fill Window with SSE2-optimized hash shifting
*
* Copyright (C) 2013 Intel Corporation
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
* Jim Kukunas <james.t.kukunas@linux.intel.com>
*
* For conditions of distribution and use, see copyright notice in zlib.h
*/
#pragma GCC target ("sse2")
#include <immintrin.h>
#include "deflate.h"
#define UPDATE_HASH(s,h,i) \
{\
if (s->level < 6) { \
h = (3483 * (s->window[i]) +\
23081* (s->window[i+1]) +\
6954 * (s->window[i+2]) +\
20947* (s->window[i+3])) & s->hash_mask;\
} else {\
h = (25881* (s->window[i]) +\
24674* (s->window[i+1]) +\
25811* (s->window[i+2])) & s->hash_mask;\
}\
}\
extern int deflate_read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
void fill_window_sse(deflate_state *s)
{
const __m128i xmm_wsize = _mm_set1_epi16(s->w_size);
register unsigned n;
register Posf *p;
unsigned more; /* Amount of free space at the end of the window. */
uInt wsize = s->w_size;
Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
do {
more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
/* Deal with !@#$% 64K limit: */
if (sizeof(int) <= 2) {
if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
more = wsize;
} else if (more == (unsigned)(-1)) {
/* Very unlikely, but possible on 16 bit machine if
* strstart == 0 && lookahead == 1 (input done a byte at time)
*/
more--;
}
}
/* If the window is almost full and there is insufficient lookahead,
* move the upper half to the lower one to make room in the upper half.
*/
if (s->strstart >= wsize+MAX_DIST(s)) {
zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
s->match_start -= wsize;
s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
s->block_start -= (long) wsize;
/* Slide the hash table (could be avoided with 32 bit values
at the expense of memory usage). We slide even when level == 0
to keep the hash table consistent if we switch back to level > 0
later. (Using level 0 permanently is not an optimal usage of
zlib, so we don't care about this pathological case.)
*/
n = s->hash_size;
p = &s->head[n];
p -= 8;
do {
__m128i value, result;
value = _mm_loadu_si128((__m128i *)p);
result = _mm_subs_epu16(value, xmm_wsize);
_mm_storeu_si128((__m128i *)p, result);
p -= 8;
n -= 8;
} while (n > 0);
n = wsize;
#ifndef FASTEST
p = &s->prev[n];
p -= 8;
do {
__m128i value, result;
value = _mm_loadu_si128((__m128i *)p);
result = _mm_subs_epu16(value, xmm_wsize);
_mm_storeu_si128((__m128i *)p, result);
p -= 8;
n -= 8;
} while (n > 0);
#endif
more += wsize;
}
if (s->strm->avail_in == 0) break;
/* If there was no sliding:
* strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
* more == window_size - lookahead - strstart
* => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
* => more >= window_size - 2*WSIZE + 2
* In the BIG_MEM or MMAP case (not yet supported),
* window_size == input_size + MIN_LOOKAHEAD &&
* strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
* Otherwise, window_size == 2*WSIZE so more >= 2.
* If there was sliding, more >= WSIZE. So in all cases, more >= 2.
*/
Assert(more >= 2, "more < 2");
n = deflate_read_buf(s->strm,
s->window + s->strstart + s->lookahead,
more);
s->lookahead += n;
/* Initialize the hash value now that we have some input: */
if (s->lookahead >= MIN_MATCH) {
uInt str = s->strstart;
s->ins_h = s->window[str];
if (str >= 1)
UPDATE_HASH(s, s->ins_h, str + 1 - (MIN_MATCH-1));
#if MIN_MATCH != 3
Call UPDATE_HASH() MIN_MATCH-3 more times
#endif
}
/* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
* but this is not important since only literal bytes will be emitted.
*/
} while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
/* If the WIN_INIT bytes after the end of the current data have never been
* written, then zero those bytes in order to avoid memory check reports of
* the use of uninitialized (or uninitialised as Julian writes) bytes by
* the longest match routines. Update the high water mark for the next
* time through here. WIN_INIT is set to MAX_MATCH since the longest match
* routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
*/
if (s->high_water < s->window_size) {
ulg curr = s->strstart + (ulg)(s->lookahead);
ulg init;
if (s->high_water < curr) {
/* Previous high water mark below current data -- zero WIN_INIT
* bytes or up to end of window, whichever is less.
*/
init = s->window_size - curr;
if (init > WIN_INIT)
init = WIN_INIT;
zmemzero(s->window + curr, (unsigned)init);
s->high_water = curr + init;
}
else if (s->high_water < (ulg)curr + WIN_INIT) {
/* High water mark at or above current data, but below current data
* plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
* to end of window, whichever is less.
*/
init = (ulg)curr + WIN_INIT - s->high_water;
if (init > s->window_size - s->high_water)
init = s->window_size - s->high_water;
zmemzero(s->window + s->high_water, (unsigned)init);
s->high_water += init;
}
}
Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
"not enough room for search");
}

View File

@ -1,4 +1,4 @@
# Copyright 2017 The Chromium Authors. All rights reserved.
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@ -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" ]
}

View File

@ -2,4 +2,5 @@ include_rules = [
'+base',
'+build',
'+testing',
"+third_party/zlib/zlib.h",
]

View File

@ -1,5 +1,8 @@
fdegros@chromium.org
noel@chromium.org
satorux@chromium.org
# compression_utils*
asvitkine@chromium.org
isherman@chromium.org
cavalcantii@chromium.org

View File

@ -1,20 +1,19 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/compression_utils.h"
#include "base/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<const char> input,
char* output_buffer,
size_t output_buffer_size,
size_t* compressed_size,
@ -25,8 +24,8 @@ bool GzipCompress(base::StringPiece input,
// uLongf can be larger than size_t.
uLongf compressed_size_long = static_cast<uLongf>(output_buffer_size);
if (zlib_internal::GzipCompressHelper(
bit_cast<Bytef*>(output_buffer), &compressed_size_long,
bit_cast<const Bytef*>(input.data()),
base::bit_cast<Bytef*>(output_buffer), &compressed_size_long,
base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size()), malloc_fn, free_fn) != Z_OK) {
return false;
}
@ -35,7 +34,11 @@ bool GzipCompress(base::StringPiece input,
return true;
}
bool GzipCompress(base::StringPiece input, std::string* output) {
bool GzipCompress(base::span<const char> input, std::string* output) {
return GzipCompress(base::as_bytes(input), output);
}
bool GzipCompress(base::span<const uint8_t> input, std::string* output) {
// Not using std::vector<> because allocation failures are recoverable,
// which is hidden by std::vector<>.
static_assert(sizeof(Bytef) == 1, "");
@ -50,9 +53,10 @@ bool GzipCompress(base::StringPiece input, std::string* output) {
return false;
}
if (zlib_internal::GzipCompressHelper(compressed_data, &compressed_data_size,
bit_cast<const Bytef*>(input.data()),
input_size, nullptr, nullptr) != Z_OK) {
if (zlib_internal::GzipCompressHelper(
compressed_data, &compressed_data_size,
base::bit_cast<const Bytef*>(input.data()), input_size, nullptr,
nullptr) != Z_OK) {
free(compressed_data);
return false;
}
@ -73,13 +77,13 @@ bool GzipCompress(base::StringPiece input, std::string* output) {
bool GzipUncompress(const std::string& input, std::string* output) {
std::string uncompressed_output;
uLongf uncompressed_size = static_cast<uLongf>(GetUncompressedSize(input));
if (uncompressed_size > uncompressed_output.max_size())
if (size_t{uncompressed_size} > uncompressed_output.max_size())
return false;
uncompressed_output.resize(uncompressed_size);
if (zlib_internal::GzipUncompressHelper(
bit_cast<Bytef*>(uncompressed_output.data()), &uncompressed_size,
bit_cast<const Bytef*>(input.data()),
base::bit_cast<Bytef*>(uncompressed_output.data()),
&uncompressed_size, base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.length())) == Z_OK) {
output->swap(uncompressed_output);
return true;
@ -87,30 +91,44 @@ bool GzipUncompress(const std::string& input, std::string* output) {
return false;
}
bool GzipUncompress(base::StringPiece input, base::StringPiece output) {
bool GzipUncompress(base::span<const char> input,
base::span<const char> output) {
return GzipUncompress(base::as_bytes(input), base::as_bytes(output));
}
bool GzipUncompress(base::span<const uint8_t> input,
base::span<const uint8_t> output) {
uLongf uncompressed_size = GetUncompressedSize(input);
if (uncompressed_size > output.size())
return false;
return zlib_internal::GzipUncompressHelper(
bit_cast<Bytef*>(output.data()), &uncompressed_size,
bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.length())) == Z_OK;
base::bit_cast<Bytef*>(output.data()), &uncompressed_size,
base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
bool GzipUncompress(base::StringPiece input, std::string* output) {
bool GzipUncompress(base::span<const char> input, std::string* output) {
return GzipUncompress(base::as_bytes(input), output);
}
bool GzipUncompress(base::span<const uint8_t> input, std::string* output) {
// Disallow in-place usage, i.e., |input| using |*output| as underlying data.
DCHECK_NE(input.data(), output->data());
DCHECK_NE(reinterpret_cast<const char*>(input.data()), output->data());
uLongf uncompressed_size = GetUncompressedSize(input);
output->resize(uncompressed_size);
return zlib_internal::GzipUncompressHelper(
bit_cast<Bytef*>(output->data()), &uncompressed_size,
bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.length())) == Z_OK;
base::bit_cast<Bytef*>(output->data()), &uncompressed_size,
base::bit_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
uint32_t GetUncompressedSize(base::StringPiece compressed_data) {
uint32_t GetUncompressedSize(base::span<const char> compressed_data) {
return GetUncompressedSize(base::as_bytes(compressed_data));
}
uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data) {
return zlib_internal::GetGzipUncompressedSize(
bit_cast<Bytef*>(compressed_data.data()), compressed_data.length());
base::bit_cast<Bytef*>(compressed_data.data()), compressed_data.size());
}
} // namespace compression

View File

@ -1,4 +1,4 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -7,7 +7,7 @@
#include <string>
#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<const char> 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<const char> input, std::string* output);
// Like the above method, but using uint8_t instead.
bool GzipCompress(base::span<const uint8_t> input, std::string* output);
// Uncompresses the data in |input| using gzip, storing the result in |output|.
// |input| and |output| are allowed to be the same string (in-place operation).
// Returns true for success.
bool GzipUncompress(const std::string& input, std::string* output);
// Like the above method, but uses base::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<const char> input,
base::span<const char> output);
// Like the above method, but using uint8_t instead.
bool GzipUncompress(base::span<const uint8_t> input,
base::span<const uint8_t> output);
// Uncompresses the data in |input| using gzip, and writes the results to
// |output|, which must NOT be the underlying string of |input|, and is resized
// if necessary.
// Returns true for success.
bool GzipUncompress(base::StringPiece input, std::string* output);
bool GzipUncompress(base::span<const char> input, std::string* output);
// Like the above method, but using uint8_t instead.
bool GzipUncompress(base::span<const uint8_t> input, std::string* output);
// Returns the uncompressed size from GZIP-compressed |compressed_data|.
uint32_t GetUncompressedSize(base::StringPiece compressed_data);
uint32_t GetUncompressedSize(base::span<const char> compressed_data);
// Like the above method, but using uint8_t instead.
uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data);
} // namespace compression

View File

@ -1,11 +1,11 @@
/* compression_utils_portable.cc
*
* Copyright 2019 The Chromium Authors. All rights reserved.
* Copyright 2019 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
#include "third_party/zlib/google/compression_utils_portable.h"
#include "compression_utils_portable.h"
#include <stddef.h>
#include <stdlib.h>
@ -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;
}

View File

@ -1,6 +1,6 @@
/* compression_utils_portable.h
*
* Copyright 2019 The Chromium Authors. All rights reserved.
* Copyright 2019 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the Chromium source repository LICENSE file.
*/
@ -9,10 +9,14 @@
#include <stdint.h>
/* TODO(cavalcantii): remove support for Chromium ever building with a system
* zlib.
*/
#if defined(USE_SYSTEM_ZLIB)
#include <zlib.h>
/* AOSP build requires relative paths. */
#else
#include "third_party/zlib/zlib.h"
#include "zlib.h"
#endif
namespace zlib_internal {

View File

@ -1,4 +1,4 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -7,10 +7,9 @@
#include <stddef.h>
#include <stdint.h>
#include <iterator>
#include <string>
#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<const char*>(kData), base::size(kData));
std::string data(reinterpret_cast<const char*>(kData), std::size(kData));
std::string compressed_data;
EXPECT_TRUE(GzipCompress(data, &compressed_data));
std::string golden_compressed_data(
reinterpret_cast<const char*>(kCompressedData),
base::size(kCompressedData));
std::size(kCompressedData));
EXPECT_EQ(golden_compressed_data, compressed_data);
}
TEST(CompressionUtilsTest, GzipUncompression) {
std::string compressed_data(reinterpret_cast<const char*>(kCompressedData),
base::size(kCompressedData));
std::size(kCompressedData));
std::string uncompressed_data;
EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data));
std::string golden_data(reinterpret_cast<const char*>(kData),
base::size(kData));
std::size(kData));
EXPECT_EQ(golden_data, uncompressed_data);
}
TEST(CompressionUtilsTest, GzipUncompressionFromStringPieceToString) {
base::StringPiece compressed_data(
reinterpret_cast<const char*>(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<const char*>(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<const char*>(kData),
base::size(kData));
std::size(kData));
const std::string golden_compressed_data(
reinterpret_cast<const char*>(kCompressedData),
base::size(kCompressedData));
std::size(kCompressedData));
std::string data(original_data);
EXPECT_TRUE(GzipCompress(data, &data));

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

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Copyright 2011 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#

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

Binary file not shown.

Binary file not shown.

View File

@ -1,20 +1,21 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip.h"
#include <list>
#include <string>
#include <vector>
#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,229 @@ std::unique_ptr<WriterDelegate> 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<base::File> OpenFilesForReading(
const std::vector<base::FilePath>& paths) override {
std::vector<base::File> 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<base::File>* const files) override {
DCHECK(files);
files->reserve(files->size() + paths.size());
for (const base::FilePath& path : paths) {
DCHECK(!path.IsAbsolute());
const base::FilePath absolute_path = src_dir_.Append(path);
if (base::DirectoryExists(absolute_path)) {
files->emplace_back();
LOG(ERROR) << "Cannot open " << Redact(path) << ": It is a directory";
} else {
const base::File& file = files->emplace_back(
absolute_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
LOG_IF(ERROR, !file.IsValid())
<< "Cannot open " << Redact(path) << ": "
<< base::File::ErrorToString(file.error_details());
}
files.push_back(std::move(file));
}
return files;
return true;
}
bool DirectoryExists(const base::FilePath& file) override {
return base::DirectoryExists(file);
}
bool List(const base::FilePath& path,
std::vector<base::FilePath>* const files,
std::vector<base::FilePath>* const subdirs) override {
DCHECK(!path.IsAbsolute());
DCHECK(files);
DCHECK(subdirs);
std::vector<DirectoryContentEntry> ListDirectoryContent(
const base::FilePath& dir) override {
std::vector<DirectoryContentEntry> files;
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<base::FilePath>* files_to_add = &params.files_to_zip();
std::vector<base::FilePath> 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<FileAccessor::DirectoryContentEntry> 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<FileAccessor::DirectoryContentEntry> 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<internal::ZipWriter> 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);
zip_writer->SetProgressCallback(params.progress_callback,
params.progress_period);
zip_writer->SetRecursive(params.recursive);
zip_writer->ContinueOnError(params.continue_on_error);
if (!params.include_hidden_files || params.filter_callback)
zip_writer->SetFilterCallback(base::BindRepeating(
[](const ZipParams* const params, const base::FilePath& path) -> bool {
return (params->include_hidden_files || !IsHiddenFile(path)) &&
(!params->filter_callback ||
params->filter_callback.Run(params->src_dir.Append(path)));
},
&params));
if (params.src_files.empty()) {
// No source items are specified. Zip the entire source directory.
zip_writer->SetRecursive(true);
if (!zip_writer->AddDirectoryContents(base::FilePath()))
return false;
} else {
// Only zip the specified source items.
if (!zip_writer->AddMixedEntries(params.src_files))
return false;
}
return zip_writer->Close();
}
bool Unzip(const base::FilePath& src_file, const base::FilePath& dest_dir) {
return UnzipWithFilterCallback(
src_file, dest_dir, base::BindRepeating(&ExcludeNoFilesFilter), true);
}
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;
}
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 (filter_cb.Run(entry_path)) {
if (reader.current_entry_info()->is_directory()) {
if (!directory_creator.Run(entry_path))
return false;
} else {
std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry_path);
if (!reader.ExtractCurrentEntry(writer.get(),
std::numeric_limits<uint64_t>::max())) {
DLOG(WARNING) << "Failed to extract " << entry_path;
return false;
}
}
} else if (log_skipped_files) {
DLOG(WARNING) << "Skipped file " << entry_path;
while (const ZipReader::Entry* const entry = reader.Next()) {
if (entry->is_unsafe) {
LOG(ERROR) << "Found unsafe entry " << Redact(entry->path) << " in ZIP";
if (!options.continue_on_error)
return false;
continue;
}
if (!reader.AdvanceToNextEntry()) {
DLOG(WARNING) << "Failed to advance to the next file";
return false;
if (options.filter && !options.filter.Run(entry->path)) {
VLOG(1) << "Skipped ZIP entry " << Redact(entry->path);
continue;
}
if (entry->is_directory) {
// It's a directory.
if (!directory_creator.Run(entry->path)) {
LOG(ERROR) << "Cannot create directory " << Redact(entry->path);
if (!options.continue_on_error)
return false;
}
continue;
}
// It's a file.
std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry->path);
if (!writer ||
(options.progress ? !reader.ExtractCurrentEntryWithListener(
writer.get(), options.progress)
: !reader.ExtractCurrentEntry(writer.get()))) {
LOG(ERROR) << "Cannot extract file " << Redact(entry->path)
<< " from ZIP";
if (!options.continue_on_error)
return false;
}
}
return 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<base::FilePath>& 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

236
deps/zlib/google/zip.h vendored
View File

@ -1,13 +1,17 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_H_
#define THIRD_PARTY_ZLIB_GOOGLE_ZIP_H_
#include <cstdint>
#include <ostream>
#include <utility>
#include <vector>
#include "base/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<const base::FilePath>;
// Abstraction for file access operation required by Zip().
//
// Can be passed to the ZipParams for providing custom access to the files,
// for example over IPC.
// 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<base::File> OpenFilesForReading(
const std::vector<base::FilePath>& paths) = 0;
virtual bool Open(Paths paths, std::vector<base::File>* files) = 0;
virtual bool DirectoryExists(const base::FilePath& path) = 0;
virtual std::vector<DirectoryContentEntry> ListDirectoryContent(
const base::FilePath& dir_path) = 0;
virtual base::Time GetLastModifiedTime(const base::FilePath& path) = 0;
// Lists contents of a directory at |path|.
virtual bool List(const base::FilePath& path,
std::vector<base::FilePath>* files,
std::vector<base::FilePath>* subdirs) = 0;
// Gets info about a file or directory.
virtual bool GetInfo(const base::FilePath& path, Info* info) = 0;
};
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_; }
// Number of file entries added to the ZIP so far.
// A file entry is added after its bytes have been processed.
int files = 0;
// Number of directory entries added to the ZIP so far.
// A directory entry is added before items in it.
int directories = 0;
// Number of errors encountered so far (files that cannot be opened,
// directories that cannot be listed).
int errors = 0;
};
// Prints Progress to output stream.
std::ostream& operator<<(std::ostream& out, const Progress& progress);
// Callback reporting the progress of a ZIP creation operation.
//
// This callback returns a boolean indicating whether the ZIP creation operation
// should continue. If it returns false once, then the ZIP creation operation is
// immediately cancelled and the callback won't be called again.
using ProgressCallback = base::RepeatingCallback<bool(const Progress&)>;
using FilterCallback = base::RepeatingCallback<bool(const base::FilePath&)>;
// ZIP creation parameters and options.
struct ZipParams {
// Source directory. Ignored if |file_accessor| is set.
base::FilePath src_dir;
// Abstraction around file system access used to read files.
// If left null, an implementation that accesses files directly is used.
FileAccessor* file_accessor = nullptr; // Not owned
// Destination file path.
// Either dest_file or dest_fd should be set, but not both.
base::FilePath dest_file;
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Destination file passed a file descriptor.
// Either dest_file or dest_fd should be set, but not both.
int dest_fd = base::kInvalidPlatformFile;
#endif
const base::FilePath& src_dir() const { return src_dir_; }
// 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;
const base::FilePath& dest_file() const { return dest_file_; }
// Filter used to exclude files from the ZIP file. This is only taken in
// account when recursively adding subdirectory contents.
FilterCallback filter_callback;
// 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<base::FilePath>& src_relative_paths) {
src_files_ = src_relative_paths;
}
const std::vector<base::FilePath>& files_to_zip() const { return src_files_; }
// Optional progress reporting callback.
ProgressCallback progress_callback;
using FilterCallback = base::RepeatingCallback<bool(const base::FilePath&)>;
void set_filter_callback(FilterCallback filter_callback) {
filter_callback_ = filter_callback;
}
const FilterCallback& filter_callback() const { return filter_callback_; }
// Progress reporting period. The final callback is always called when the ZIP
// creation operation completes.
base::TimeDelta progress_period;
void set_include_hidden_files(bool include_hidden_files) {
include_hidden_files_ = include_hidden_files;
}
bool include_hidden_files() const { return include_hidden_files_; }
// Should add hidden files? This is only taken in account when recursively
// adding subdirectory contents.
bool include_hidden_files = true;
// 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<FileAccessor> file_accessor) {
file_accessor_ = std::move(file_accessor);
}
FileAccessor* file_accessor() const { return file_accessor_.get(); }
// Should recursively add subdirectory contents?
bool recursive = false;
private:
base::FilePath src_dir_;
base::FilePath dest_file_;
#if defined(OS_POSIX)
int dest_fd_ = base::kInvalidPlatformFile;
#endif
// 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<base::FilePath> src_files_;
// Filter used to exclude files from the ZIP file. Only effective when
// |src_files_| is empty.
FilterCallback filter_callback_;
// Whether hidden files should be included in the ZIP file. Only effective
// when |src_files_| is empty.
bool include_hidden_files_ = true;
// Abstraction around file system access used to read files. An implementation
// that accesses files directly is provided by default.
std::unique_ptr<FileAccessor> file_accessor_;
// 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,71 @@ 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(const base::FilePath&)>;
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<base::FilePath>& src_relative_paths,
Paths src_relative_paths,
int dest_fd);
#endif // defined(OS_POSIX)
#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
// 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(const base::FilePath&)>;
bool UnzipWithFilterCallback(const base::FilePath& zip_file,
const base::FilePath& dest_dir,
const FilterCallback& filter_cb,
bool log_skipped_files);
// Callback reporting the number of bytes written during Unzip.
using UnzipProgressCallback = base::RepeatingCallback<void(uint64_t bytes)>;
// Options of the Unzip function, with valid default values.
struct UnzipOptions {
// Encoding of entry paths in the ZIP archive. By default, paths are assumed
// to be in UTF-8.
std::string encoding;
// Only extract the entries for which |filter_cb| returns true. By default,
// everything gets extracted.
FilterCallback filter;
// Callback to report bytes extracted from the ZIP.
UnzipProgressCallback progress;
// Password to decrypt the encrypted files.
std::string password;
// Should ignore errors when extracting files?
bool continue_on_error = false;
};
// 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<std::unique_ptr<WriterDelegate>(
const base::FilePath&)>
WriterFactory;
typedef base::RepeatingCallback<bool(const base::FilePath&)> 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);
typedef base::RepeatingCallback<bool(const base::FilePath&)> DirectoryCreator;
// Unzips the contents of |zip_file|, using the writers provided by
// |writer_factory|.
bool Unzip(const base::PlatformFile& zip_file,
WriterFactory writer_factory,
DirectoryCreator directory_creator,
UnzipOptions options = {});
// Unzips the contents of |zip_file| into |dest_dir|.
// This function does not overwrite any existing file.
// A filename collision will result in an error.
// Therefore, |dest_dir| should initially be an empty directory.
bool Unzip(const base::FilePath& zip_file,
const base::FilePath& dest_dir,
UnzipOptions options = {});
} // namespace zip

View File

@ -1,14 +1,20 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip_internal.h"
#include <stddef.h>
#include <string.h>
#include <algorithm>
#include <unordered_set>
#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<const char*>(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<FILE*>(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<int*>(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<HANDLE>(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<ZipBuffer*>(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<uLong>(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<ZipBuffer*>(opaque);
if (!buffer)
return -1;
return static_cast<long>(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<ZipBuffer*>(opaque);
if (!buffer)
return -1;
if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
buffer->offset = std::min(buffer->offset + static_cast<size_t>(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<size_t>(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<void*>(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<StringPiece, base::StringPieceHashImpl<StringPiece>>>
exts(std::initializer_list<StringPiece>{
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

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -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;

View File

@ -1,20 +1,26 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip_reader.h"
#include <algorithm>
#include <utility>
#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 <sys/stat.h>
#endif
namespace zip {
namespace {
// StringWriterDelegate --------------------------------------------------------
enum UnzipError : int;
// 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;
// WriterDelegate methods:
// Returns true.
bool PrepareOutput() override;
// 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;
void SetTimeModified(const base::Time& time) override;
private:
size_t max_read_bytes_;
std::string* output_;
DISALLOW_COPY_AND_ASSIGN(StringWriterDelegate);
};
StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes,
std::string* output)
: max_read_bytes_(max_read_bytes),
output_(output) {
std::ostream& operator<<(std::ostream& out, UnzipError error) {
#define SWITCH_ERR(X) \
case X: \
return out << #X;
switch (error) {
SWITCH_ERR(UNZ_OK);
SWITCH_ERR(UNZ_END_OF_LIST_OF_FILE);
SWITCH_ERR(UNZ_ERRNO);
SWITCH_ERR(UNZ_PARAMERROR);
SWITCH_ERR(UNZ_BADZIPFILE);
SWITCH_ERR(UNZ_INTERNALERROR);
SWITCH_ERR(UNZ_CRCERROR);
default:
return out << "UNZ" << static_cast<int>(error);
}
#undef SWITCH_ERR
}
StringWriterDelegate::~StringWriterDelegate() {
}
bool StringWriterDelegate::PrepareOutput() {
return true;
}
bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) {
if (output_->size() + num_bytes > max_read_bytes_)
bool IsValidFileNameCharacterOnWindows(char16_t c) {
if (c < 32)
return false;
output_->append(data, num_bytes);
return true;
}
void StringWriterDelegate::SetTimeModified(const base::Time& time) {
// Do nothing.
}
} // 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;
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;
}
// 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();
return true;
}
// A writer delegate that writes to a given string.
class StringWriterDelegate : public WriterDelegate {
public:
explicit StringWriterDelegate(std::string* output) : output_(output) {}
// WriterDelegate methods:
bool WriteBytes(const char* data, int num_bytes) override {
output_->append(data, num_bytes);
return true;
}
private:
std::string* const output_;
};
#if defined(OS_POSIX)
void SetPosixFilePermissions(int fd, int mode) {
base::stat_wrapper_t sb;
if (base::File::Fstat(fd, &sb)) {
return;
}
mode_t new_mode = sb.st_mode;
// Transfer the executable bit only if the file is readable.
if ((sb.st_mode & S_IRUSR) == S_IRUSR && (mode & S_IXUSR) == S_IXUSR) {
new_mode |= S_IXUSR;
}
if ((sb.st_mode & S_IRGRP) == S_IRGRP && (mode & S_IXGRP) == S_IXGRP) {
new_mode |= S_IXGRP;
}
if ((sb.st_mode & S_IROTH) == S_IROTH && (mode & S_IXOTH) == S_IXOTH) {
new_mode |= S_IXOTH;
}
if (new_mode != sb.st_mode) {
fchmod(fd, new_mode);
}
}
#endif
} // namespace
ZipReader::ZipReader() {
Reset();
}
@ -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,145 +166,329 @@ 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();
next_index_++;
if (!OpenEntry()) {
reached_end_ = true;
ok_ = false;
return nullptr;
}
return &entry_;
}
bool ZipReader::OpenEntry() {
DCHECK(zip_file_);
// Get entry info.
unz_file_info64 info = {};
char path_in_zip[internal::kZipMaxPath] = {};
if (const UnzipError err{unzGetCurrentFileInfo64(
zip_file_, &info, path_in_zip, sizeof(path_in_zip) - 1, nullptr, 0,
nullptr, 0)};
err != UNZ_OK) {
LOG(ERROR) << "Cannot get entry from ZIP: " << err;
return false;
}
entry_.path_in_original_encoding = path_in_zip;
// Convert path from original encoding to Unicode.
std::u16string path_in_utf16;
const char* const encoding = encoding_.empty() ? "UTF-8" : encoding_.c_str();
if (!base::CodepageToUTF16(entry_.path_in_original_encoding, encoding,
base::OnStringConversionError::SUBSTITUTE,
&path_in_utf16)) {
LOG(ERROR) << "Cannot convert path from encoding " << encoding;
return false;
}
// Normalize path.
Normalize(path_in_utf16);
entry_.original_size = info.uncompressed_size;
// The file content of this entry is encrypted if flag bit 0 is set.
entry_.is_encrypted = info.flag & 1;
if (entry_.is_encrypted) {
// Is the entry AES encrypted.
entry_.uses_aes_encryption = info.compression_method == 99;
} else {
entry_.uses_aes_encryption = false;
}
// Construct the last modified time. The timezone info is not present in ZIP
// archives, so we construct the time as UTC.
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;
}
bool ZipReader::OpenCurrentEntryInZip() {
DCHECK(zip_file_);
void ZipReader::Normalize(base::StringPiece16 in) {
entry_.is_unsafe = true;
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)
return false;
if (raw_file_name_in_zip[0] == '\0')
return false;
current_entry_info_.reset(
new EntryInfo(raw_file_name_in_zip, raw_file_info));
return 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;
}
void ZipReader::ReportProgress(ListenerCallback listener_callback,
uint64_t bytes) const {
delta_bytes_read_ += bytes;
const base::TimeTicks now = base::TimeTicks::Now();
if (next_progress_report_time_ > now)
return;
next_progress_report_time_ = now + progress_period_;
listener_callback.Run(delta_bytes_read_);
delta_bytes_read_ = 0;
}
bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
ListenerCallback listener_callback,
uint64_t num_bytes_to_extract) const {
DCHECK(zip_file_);
DCHECK_LT(0, next_index_);
DCHECK(ok_);
DCHECK(!reached_end_);
const int open_result = unzOpenCurrentFile(zip_file_);
if (open_result != UNZ_OK)
// 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<char[]> 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.
break;
} else if (num_bytes_read > 0) {
uint64_t num_bytes_to_write = std::min<uint64_t>(
remaining_capacity, base::checked_cast<uint64_t>(num_bytes_read));
if (!delegate->WriteBytes(buf.get(), num_bytes_to_write))
break;
if (remaining_capacity == base::checked_cast<uint64_t>(num_bytes_read)) {
// Ensures function returns true if the entire file has been read.
entire_file_extracted =
(unzReadCurrentFile(zip_file_, buf.get(), 1) == 0);
}
CHECK_GE(remaining_capacity, num_bytes_to_write);
remaining_capacity -= num_bytes_to_write;
}
if (num_bytes_read < 0) {
LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
<< " from ZIP: " << UnzipError(num_bytes_read);
break;
}
if (listener_callback) {
ReportProgress(listener_callback, num_bytes_read);
}
DCHECK_LT(0, num_bytes_read);
CHECK_LE(num_bytes_read, internal::kZipBufSize);
uint64_t num_bytes_to_write = std::min<uint64_t>(
remaining_capacity, base::checked_cast<uint64_t>(num_bytes_read));
if (!delegate->WriteBytes(buf, num_bytes_to_write))
break;
if (remaining_capacity == base::checked_cast<uint64_t>(num_bytes_read)) {
// Ensures function returns true if the entire file has been read.
const int n = unzReadCurrentFile(zip_file_, buf, 1);
entire_file_extracted = (n == 0);
LOG_IF(ERROR, n < 0) << "Cannot read file " << Redact(entry_.path)
<< " from ZIP: " << UnzipError(n);
}
CHECK_GE(remaining_capacity, num_bytes_to_write);
remaining_capacity -= num_bytes_to_write;
}
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();
}
if (listener_callback) {
listener_callback.Run(delta_bytes_read_);
delta_bytes_read_ = 0;
}
return entire_file_extracted;
}
bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
uint64_t num_bytes_to_extract) const {
return ExtractCurrentEntry(delegate, ListenerCallback(),
num_bytes_to_extract);
}
bool ZipReader::ExtractCurrentEntryWithListener(
WriterDelegate* delegate,
ListenerCallback listener_callback) const {
return ExtractCurrentEntry(delegate, listener_callback);
}
void ZipReader::ExtractCurrentEntryToFilePathAsync(
const base::FilePath& output_file_path,
SuccessCallback success_callback,
FailureCallback failure_callback,
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 +496,155 @@ 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_);
if (max_read_bytes == 0) {
output->clear();
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<size_t>(std::min(base::checked_cast<int64_t>(max_read_bytes),
current_entry_info()->original_size())));
// The original_size is the best hint for the real size, so it saves doing
// reallocations for the common case when the uncompressed size is correct.
// However, we need to assume that the uncompressed size could be incorrect
// therefore this function needs to read as much data as possible.
output->reserve(base::checked_cast<size_t>(std::min<uint64_t>(
max_read_bytes, base::checked_cast<uint64_t>(entry_.original_size))));
StringWriterDelegate writer(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;
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;
delta_bytes_read_ = 0;
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;
progress_callback.Run(current_progress);
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));
std::move(success_callback).Run();
return;
}
if (num_bytes_read < 0) {
LOG(ERROR) << "Cannot read file " << Redact(entry_.path)
<< " from ZIP: " << UnzipError(num_bytes_read);
std::move(failure_callback).Run();
return;
}
if (num_bytes_read != output_file.Write(offset, buffer, num_bytes_read)) {
LOG(ERROR) << "Cannot write " << num_bytes_read
<< " bytes to file at offset " << offset;
std::move(failure_callback).Run();
return;
}
offset += num_bytes_read;
progress_callback.Run(offset);
base::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<base::File> file)
: file_(file.get()), owned_file_(std::move(file)) {}
FileWriterDelegate::~FileWriterDelegate() {
if (!file_->SetLength(file_length_)) {
DVPLOG(1) << "Failed updating length of written file";
}
FileWriterDelegate::FileWriterDelegate(base::File* file) : file_(file) {
DCHECK(file_);
}
FileWriterDelegate::FileWriterDelegate(base::File owned_file)
: owned_file_(std::move(owned_file)) {
DCHECK_EQ(file_, &owned_file_);
}
FileWriterDelegate::~FileWriterDelegate() {}
bool FileWriterDelegate::PrepareOutput() {
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 +658,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;
}
return true;
}
bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
}
void FilePathWriterDelegate::OnError() {
FileWriterDelegate::OnError();
owned_file_.Close();
void FilePathWriterDelegate::SetTimeModified(const base::Time& time) {
file_.Close();
base::TouchFile(output_file_path_, base::Time::Now(), time);
if (!base::DeleteFile(output_file_path_)) {
LOG(ERROR) << "Cannot delete partially extracted file "
<< Redact(output_file_path_);
}
}
} // namespace zip

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
@ -7,15 +7,15 @@
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <memory>
#include <string>
#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<uint64_t>::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:
@ -71,63 +85,77 @@ class ZipReader {
// A callback that is called periodically during the operation with the number
// of bytes that have been processed so far.
using ProgressCallback = base::RepeatingCallback<void(int64_t)>;
// A callback that is called periodically during the operation with the number
// of bytes that have been processed since the previous call (i.e. delta).
using ListenerCallback = base::RepeatingCallback<void(uint64_t)>;
// 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);
// 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;
// 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_; }
// 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;
// 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_; }
// 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;
// Returns the last modified time. If the time stored in the zip file was
// not valid, the unix epoch will be returned.
// 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_; }
// * 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;
// Returns true if the entry is a directory.
bool is_directory() const { return is_directory_; }
// True if the entry is a directory.
// False if the entry is a file.
bool is_directory = false;
// 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_; }
// True if the entry path cannot be converted to a safe relative path. This
// happens if a file entry (not a directory) has a filename "." or "..".
bool is_unsafe = false;
// Returns true if the entry is encrypted.
bool is_encrypted() const { return is_encrypted_; }
// True if the file content is encrypted.
bool is_encrypted = false;
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);
// True if the encryption scheme is AES.
bool uses_aes_encryption = false;
// Entry POSIX permissions (POSIX systems only).
int posix_mode;
};
ZipReader();
ZipReader(const ZipReader&) = delete;
ZipReader& operator=(const ZipReader&) = delete;
~ZipReader();
// Opens the zip 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 +164,105 @@ 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:
// Sets the encoding of entry paths in the ZIP archive.
// By default, paths are assumed to be in UTF-8.
void SetEncoding(std::string encoding) { encoding_ = std::move(encoding); }
// Sets the decryption password that will be used to decrypt encrypted file in
// the ZIP archive.
void SetPassword(std::string password) { password_ = std::move(password); }
// Gets the next entry. Returns null if there is no more entry, or if an error
// occurred while scanning entries. The returned Entry is owned by this
// ZipReader, and is valid until Next() is called again or until this
// ZipReader is closed.
//
// while (reader.HasMore()) {
// // Do something with the current file here.
// reader.AdvanceToNextEntry();
// This function should be called before operations over the current entry
// like ExtractCurrentEntryToFile().
//
// while (const ZipReader::Entry* entry = reader.Next()) {
// // Do something with the current entry here.
// ...
// }
bool HasMore();
// Advances the next entry. Returns true on success.
bool AdvanceToNextEntry();
// 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().
//
// Note that there is no CloseCurrentEntryInZip(). The the current entry
// state is reset automatically as needed.
bool OpenCurrentEntryInZip();
// // 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<uint64_t>::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.
// Extracts the current entry to |delegate|, starting from the beginning
// of the entry, calling |listener_callback| regularly with the number of
// bytes extracted.
//
// Returns true if the entire file was extracted without error.
//
// Precondition: Next() returned a non-null Entry.
bool ExtractCurrentEntryWithListener(
WriterDelegate* delegate,
ListenerCallback listener_callback) const;
// Asynchronously extracts the current entry to the given output file path. If
// the current entry is a directory it just creates the directory
// synchronously instead.
//
// |success_callback| will be called on success and |failure_callback| will be
// called on failure. |progress_callback| will be called at least once.
// Callbacks will be posted to the current MessageLoop in-order.
//
// Precondition: Next() returned a non-null Entry.
void ExtractCurrentEntryToFilePathAsync(
const base::FilePath& output_file_path,
SuccessCallback success_callback,
FailureCallback failure_callback,
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<uint64_t>(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 +272,64 @@ 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);
// Runs the ListenerCallback at a throttled rate.
void ReportProgress(ListenerCallback listener_callback, uint64_t bytes) const;
// Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|,
// starting from the beginning of the entry calling |listener_callback| if
// its supplied.
//
// Returns true if the entire file was extracted without error.
//
// Precondition: Next() returned a non-null Entry.
bool ExtractCurrentEntry(WriterDelegate* delegate,
ListenerCallback listener_callback,
uint64_t num_bytes_to_extract =
std::numeric_limits<uint64_t>::max()) const;
// Extracts a chunk of the file to the target. Will post a task for the next
// chunk and success/failure/progress callbacks as necessary.
void ExtractChunk(base::File target_file,
SuccessCallback success_callback,
FailureCallback failure_callback,
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<EntryInfo> current_entry_info_;
bool ok_;
Entry entry_;
// Next time to report progress.
mutable base::TimeTicks next_progress_report_time_ = base::TimeTicks::Now();
// Progress time delta.
// TODO(crbug.com/953256) Add this as parameter to the unzip options.
base::TimeDelta progress_period_ = base::Milliseconds(1000);
// Number of bytes read since last progress report callback executed.
mutable uint64_t delta_bytes_read_ = 0;
base::WeakPtrFactory<ZipReader> weak_ptr_factory_{this};
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 +338,14 @@ class FileWriterDelegate : public WriterDelegate {
explicit FileWriterDelegate(base::File* file);
// Constructs a FileWriterDelegate that takes ownership of |file|.
explicit FileWriterDelegate(std::unique_ptr<base::File> file);
explicit FileWriterDelegate(base::File owned_file);
FileWriterDelegate(const FileWriterDelegate&) = delete;
FileWriterDelegate& operator=(const FileWriterDelegate&) = delete;
// Truncates the file to the number of bytes written.
~FileWriterDelegate() override;
// WriterDelegate methods:
// 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 +355,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.
// On POSIX systems, sets the file to be executable if the source file was
// executable.
void SetPosixFilePermissions(int mode) override;
// Empties the file to avoid leaving garbage data in it.
void OnError() override;
// Gets the number of bytes written into the file.
int64_t file_length() { return file_length_; }
private:
// The file the delegate modifies.
base::File* file_;
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<base::File> owned_file_;
base::File owned_file_;
// The file the delegate modifies.
base::File* const file_ = &owned_file_;
int64_t file_length_ = 0;
DISALLOW_COPY_AND_ASSIGN(FileWriterDelegate);
};
// 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);
explicit FilePathWriterDelegate(base::FilePath output_file_path);
FilePathWriterDelegate(const FilePathWriterDelegate&) = delete;
FilePathWriterDelegate& operator=(const FilePathWriterDelegate&) = delete;
~FilePathWriterDelegate() override;
// WriterDelegate methods:
// Creates the output file and any necessary intermediate directories.
// 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;
// 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;
// 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

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -8,29 +8,36 @@
#include <stdint.h>
#include <string.h>
#include <set>
#include <iterator>
#include <string>
#include <vector>
#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<MockUnzipListener> {
: 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<uint64_t>::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<base::FilePath>;
} // 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;
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;
if (ZipReader reader; reader.Open(zip_path)) {
if (!encoding.empty())
reader.SetEncoding(std::string(encoding));
while (const ZipReader::Entry* const entry = reader.Next()) {
EXPECT_TRUE(reader.ok());
paths.push_back(entry->path);
}
EXPECT_TRUE(reader.ok());
}
return paths;
}
// The path to temporary directory used to contain the test operations.
base::FilePath test_dir_;
// The path to the test data directory where test.zip etc. are located.
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<base::FilePath> 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, Iteration) {
std::set<base::FilePath> actual_contents;
TEST_F(ZipReaderTest, Open_EmptyFile) {
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.AdvanceToNextEntry()); // Shouldn't go further.
EXPECT_EQ(test_zip_contents_.size(),
static_cast<size_t>(reader.num_entries()));
EXPECT_EQ(test_zip_contents_.size(), actual_contents.size());
EXPECT_EQ(test_zip_contents_, actual_contents);
EXPECT_FALSE(reader.ok());
EXPECT_FALSE(reader.Open(data_dir_.AppendASCII("empty.zip")));
EXPECT_FALSE(reader.ok());
}
// Open the test zip file from a file descriptor, iterate through its contents,
// and compare that they match the expected contents.
// Iterate through the contents in the test ZIP archive, and compare that the
// contents collected from the ZipReader matches the expected contents.
TEST_F(ZipReaderTest, Iteration) {
Paths actual_contents;
ZipReader reader;
EXPECT_FALSE(reader.ok());
EXPECT_TRUE(reader.Open(test_zip_file_));
EXPECT_TRUE(reader.ok());
while (const ZipReader::Entry* const entry = reader.Next()) {
EXPECT_TRUE(reader.ok());
actual_contents.push_back(entry->path);
}
EXPECT_TRUE(reader.ok());
EXPECT_FALSE(reader.Next()); // Shouldn't go further.
EXPECT_TRUE(reader.ok());
EXPECT_THAT(actual_contents, SizeIs(reader.num_entries()));
EXPECT_THAT(actual_contents, ElementsAreArray(test_zip_contents_));
}
// Open the test ZIP archive from a file descriptor, iterate through its
// contents, and compare that they match the expected contents.
TEST_F(ZipReaderTest, PlatformFileIteration) {
std::set<base::FilePath> 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<size_t>(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();
// 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());
ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil_via_invalid_utf8.zip")));
base::FilePath target_path = base::FilePath::FromUTF8Unsafe(".<2E>.<2E>evil.txt");
const ZipReader::Entry* entry = LocateAndOpenEntry(&reader, target_path);
ASSERT_TRUE(entry);
EXPECT_EQ(target_path, entry->path);
EXPECT_FALSE(entry->is_unsafe);
EXPECT_FALSE(entry->is_directory);
}
TEST_F(ZipReaderTest, current_entry_info_AbsoluteFile) {
// By default, file paths in ZIPs are interpreted as UTF-8. But in this test,
// the ZIP archive contains file paths that are actually encoded in Shift JIS.
// The SJIS-encoded paths are thus wrongly interpreted as UTF-8, resulting in
// garbled paths. Invalid UTF-8 sequences are safely converted to the
// replacement character <20>.
TEST_F(ZipReaderTest, EncodingSjisAsUtf8) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip")),
ElementsAre(
base::FilePath::FromUTF8Unsafe("<EFBFBD>V<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>t<EFBFBD>H<EFBFBD><EFBFBD><EFBFBD>_/SJIS_835C_<43><5F>.txt"),
base::FilePath::FromUTF8Unsafe(
"<EFBFBD>V<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>t<EFBFBD>H<EFBFBD><EFBFBD><EFBFBD>_/<2F>V<EFBFBD><56><EFBFBD><EFBFBD><EFBFBD>e<EFBFBD>L<EFBFBD>X<EFBFBD>g <20>h<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>g.txt")));
}
// In this test, SJIS-encoded paths are interpreted as Code Page 1252. This
// results in garbled paths. Note the presence of C1 control codes U+0090 and
// U+0081 in the garbled paths.
TEST_F(ZipReaderTest, EncodingSjisAs1252) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "windows-1252"),
ElementsAre(base::FilePath::FromUTF8Unsafe(
"\u0090Vµ¢ƒtƒHƒƒ_/SJIS_835C_ƒ<5F>.txt"),
base::FilePath::FromUTF8Unsafe(
"\u0090Vµ¢ƒtƒHƒƒ_/\u0090Vµ¢ƒeƒLƒXƒg "
"ƒhƒLƒ…ƒ\u0081ƒ“ƒg.txt")));
}
// In this test, SJIS-encoded paths are interpreted as Code Page 866. This
// results in garbled paths.
TEST_F(ZipReaderTest, EncodingSjisAsIbm866) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "IBM866"),
ElementsAre(
base::FilePath::FromUTF8Unsafe("РVВВвГtГHГЛГ_/SJIS_835C_Г<5F>.txt"),
base::FilePath::FromUTF8Unsafe(
"РVВВвГtГHГЛГ_/РVВВвГeГLГXГg ГhГLГЕГБГУГg.txt")));
}
// Tests that SJIS-encoded paths are correctly converted to Unicode.
TEST_F(ZipReaderTest, EncodingSjis) {
EXPECT_THAT(
GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "Shift_JIS"),
ElementsAre(
base::FilePath::FromUTF8Unsafe("新しいフォルダ/SJIS_835C_ソ.txt"),
base::FilePath::FromUTF8Unsafe(
"新しいフォルダ/新しいテキスト ドキュメント.txt")));
}
TEST_F(ZipReaderTest, AbsoluteFile) {
ZipReader reader;
ASSERT_TRUE(reader.Open(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());
// This file is unsafe because of the absolute file name.
EXPECT_TRUE(current_entry_info->is_unsafe());
EXPECT_FALSE(current_entry_info->is_directory());
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<MockWriterDelegate> 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<uint64_t>::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<MockWriterDelegate> 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<uint64_t>::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<MockWriterDelegate> 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<uint64_t>::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

File diff suppressed because it is too large Load Diff

View File

@ -1,203 +1,308 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/zlib/google/zip_writer.h"
#include <algorithm>
#include "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;
// Numbers of pending entries that trigger writting them to the ZIP file.
constexpr size_t kMaxPendingEntriesCount = 50;
const base::TimeTicks now = base::TimeTicks::Now();
if (next_progress_report_time_ > now)
return true;
bool AddFileContentToZip(zipFile zip_file,
base::File file,
const base::FilePath& file_path) {
int num_bytes;
char buf[zip::internal::kZipBufSize];
do {
num_bytes = file.ReadAtCurrentPos(buf, zip::internal::kZipBufSize);
next_progress_report_time_ = now + progress_period_;
if (progress_callback_.Run(progress_))
return true;
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 (num_bytes > 0);
return true;
LOG(ERROR) << "Cancelling ZIP creation";
return false;
}
bool OpenNewFileEntry(zipFile zip_file,
const base::FilePath& path,
bool is_directory,
base::Time last_modified) {
bool ZipWriter::AddFileContent(const base::FilePath& path, base::File file) {
char buf[zip::internal::kZipBufSize];
while (ShouldContinue()) {
const int num_bytes =
file.ReadAtCurrentPos(buf, zip::internal::kZipBufSize);
if (num_bytes < 0) {
PLOG(ERROR) << "Cannot read file " << Redact(path);
return false;
}
if (num_bytes == 0)
return true;
if (zipWriteInFileInZip(zip_file_, buf, num_bytes) != ZIP_OK) {
PLOG(ERROR) << "Cannot write data from file " << Redact(path)
<< " to ZIP";
return false;
}
progress_.bytes += num_bytes;
}
return false;
}
bool ZipWriter::OpenNewFileEntry(const base::FilePath& path,
bool is_directory,
base::Time last_modified) {
std::string str_path = path.AsUTF8Unsafe();
#if defined(OS_WIN)
base::ReplaceSubstringsAfterOffset(&str_path, 0u, "\\", "/");
#endif
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_;
}
if (!OpenNewFileEntry(path, /*is_directory=*/true, info.last_modified))
return false;
if (!CloseNewFileEntry())
return false;
progress_.directories++;
if (!ShouldContinue())
return false;
if (!recursive_)
return true;
return AddDirectoryContents(path);
}
} // namespace
#if defined(OS_POSIX)
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// static
std::unique_ptr<ZipWriter> 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<ZipWriter>(
new ZipWriter(zip_file, root_dir, file_accessor));
return std::unique_ptr<ZipWriter>(new ZipWriter(zip_file, file_accessor));
}
#endif
// static
std::unique_ptr<ZipWriter> ZipWriter::Create(
const base::FilePath& zip_file_path,
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<ZipWriter>(
new ZipWriter(zip_file, root_dir, file_accessor));
return std::unique_ptr<ZipWriter>(new ZipWriter(zip_file, 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(zipFile zip_file, FileAccessor* file_accessor)
: zip_file_(zip_file), file_accessor_(file_accessor) {}
ZipWriter::~ZipWriter() {
DCHECK(pending_entries_.empty());
}
bool ZipWriter::WriteEntries(const std::vector<base::FilePath>& paths) {
return AddEntries(paths) && Close();
}
bool ZipWriter::AddEntries(const std::vector<base::FilePath>& paths) {
DCHECK(zip_file_);
pending_entries_.insert(pending_entries_.end(), paths.begin(), paths.end());
return FlushEntriesIfNeeded(/*force=*/false);
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<const base::FilePath*> directories;
while (pending_entries_.size() >= kMaxPendingEntriesCount ||
(force && !pending_entries_.empty())) {
size_t entry_count =
std::min(pending_entries_.size(), kMaxPendingEntriesCount);
std::vector<base::FilePath> relative_paths;
std::vector<base::FilePath> 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);
// Declared outside loop to reuse internal buffer.
std::vector<base::File> files;
// 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<base::File> files =
file_accessor_->OpenFilesForReading(absolute_paths);
DCHECK_EQ(files.size(), relative_paths.size());
for (size_t i = 0; i < files.size(); i++) {
// First pass. We don't know which paths are files and which ones are
// directories, and we want to avoid making a call to file_accessor_ for each
// path. Try to open all of the paths as files. We'll get invalid file
// descriptors for directories, and we'll process these directories in the
// second pass.
while (!paths.empty()) {
// Work with chunks of 50 paths at most.
const size_t n = std::min<size_t>(paths.size(), 50);
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<base::File> files;
while (!paths.empty()) {
// Work with chunks of 50 paths at most.
const size_t n = std::min<size_t>(paths.size(), 50);
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<base::FilePath> files, subdirs;
if (!file_accessor_->List(path, &files, &subdirs)) {
progress_.errors++;
return continue_on_error_;
}
Filter(&files);
Filter(&subdirs);
if (!AddFileEntries(files))
return false;
return AddDirectoryEntries(subdirs);
}
void ZipWriter::Filter(std::vector<base::FilePath>* const paths) {
DCHECK(paths);
if (!filter_callback_)
return;
const auto end = std::remove_if(paths->begin(), paths->end(),
[this](const base::FilePath& path) {
return !filter_callback_.Run(path);
});
paths->erase(end, paths->end());
}
} // namespace internal
} // namespace zip

View File

@ -1,4 +1,4 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -9,6 +9,7 @@
#include <vector>
#include "base/files/file_path.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "third_party/zlib/google/zip.h"
@ -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<ZipWriter> CreateWithFd(int zip_file_fd,
const base::FilePath& root_dir,
FileAccessor* file_accessor);
#endif
static std::unique_ptr<ZipWriter> 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<base::FilePath>& 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);
}
private:
ZipWriter(zipFile zip_file,
const base::FilePath& root_dir,
FileAccessor* file_accessor);
// Should ignore missing files and directories?
void ContinueOnError(bool continue_on_error) {
continue_on_error_ = continue_on_error;
}
// 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);
// Sets the recursive flag, indicating whether the contents of subdirectories
// should be included.
void SetRecursive(bool b) { recursive_ = b; }
// 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<base::FilePath>& paths);
// 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.
// Returns true if successful, false otherwise (typically if an entry failed
// to be written).
bool Close();
// The entries that have been added but not yet written to the ZIP file.
std::vector<base::FilePath> pending_entries_;
private:
// Takes ownership of |zip_file|.
ZipWriter(zipFile zip_file, FileAccessor* file_accessor);
// Regularly called during processing to check whether zipping should continue
// or should be cancelled.
bool ShouldContinue();
// Adds file content to currently open file entry.
bool AddFileContent(const base::FilePath& path, base::File file);
// Adds a file entry (including file contents).
bool AddFileEntry(const base::FilePath& path, base::File file);
// Adds file entries. All the paths should be existing files.
bool AddFileEntries(Paths paths);
// Adds a directory entry. If the recursive flag is set, the contents of this
// directory are also added.
bool AddDirectoryEntry(const base::FilePath& path);
// Adds directory entries. All the paths should be existing directories. If
// the recursive flag is set, the contents of these directories are also
// added.
bool AddDirectoryEntries(Paths paths);
// Opens a file or directory entry.
bool OpenNewFileEntry(const base::FilePath& path,
bool is_directory,
base::Time last_modified);
// Closes the currently open entry.
bool CloseNewFileEntry();
// Filters entries.
void Filter(std::vector<base::FilePath>* paths);
// The actual zip file.
zipFile zip_file_;
// 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_;
DISALLOW_COPY_AND_ASSIGN(ZipWriter);
// Progress stats.
Progress progress_;
// Optional progress callback.
ProgressCallback progress_callback_;
// Optional progress reporting period.
base::TimeDelta progress_period_;
// Next time to report progress.
base::TimeTicks next_progress_report_time_ = base::TimeTicks::Now();
// Filter used to exclude files from the ZIP file.
FilterCallback filter_callback_;
// Should recursively add directories?
bool recursive_ = false;
// Should ignore missing files and directories?
bool continue_on_error_ = false;
};
} // namespace internal
} // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_WRITER_H_
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_WRITER_H_

5
deps/zlib/gzguts.h vendored
View File

@ -1,5 +1,5 @@
/* gzguts.h -- zlib internal header definitions for gz* operations
* Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
* Copyright (C) 2004-2019 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -39,7 +39,7 @@
# include <io.h>
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
#if defined(_WIN32)
# define WIDECHAR
#endif
@ -190,6 +190,7 @@ typedef struct {
/* just for writing */
int level; /* compression level */
int strategy; /* compression strategy */
int reset; /* true if a reset is pending after a Z_FINISH */
/* seek request */
z_off64_t skip; /* amount to skip (already rewound if backwards) */
int seek; /* true if seek request pending */

10
deps/zlib/gzlib.c vendored
View File

@ -1,11 +1,11 @@
/* gzlib.c -- zlib functions common to reading and writing gzip files
* Copyright (C) 2004-2017 Mark Adler
* Copyright (C) 2004-2019 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
#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
@ -30,7 +30,7 @@ local gzFile gz_open OF((const void *, int, const char *));
The gz_strwinerror function does not change the current setting of
GetLastError. */
char ZLIB_INTERNAL *gz_strwinerror (error)
char ZLIB_INTERNAL *gz_strwinerror(error)
DWORD error;
{
static char buf[1024];
@ -81,6 +81,8 @@ local void gz_reset(state)
state->past = 0; /* have not read past end yet */
state->how = LOOK; /* look for gzip header */
}
else /* for writing ... */
state->reset = 0; /* no deflateReset pending */
state->seek = 0; /* no seek request pending */
gz_error(state, Z_OK, NULL); /* clear error */
state->x.pos = 0; /* no uncompressed data yet */
@ -397,7 +399,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;

20
deps/zlib/gzread.c vendored
View File

@ -1,5 +1,5 @@
/* gzread.c -- zlib functions for reading gzip files
* Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
* Copyright (C) 2004-2017 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -157,11 +157,9 @@ local int gz_look(state)
the output buffer is larger than the input buffer, which also assures
space for gzungetc() */
state->x.next = state->out;
if (strm->avail_in) {
memcpy(state->x.next, strm->next_in, strm->avail_in);
state->x.have = strm->avail_in;
strm->avail_in = 0;
}
memcpy(state->x.next, strm->next_in, strm->avail_in);
state->x.have = strm->avail_in;
strm->avail_in = 0;
state->how = COPY;
state->direct = 1;
return 0;
@ -314,9 +312,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 +395,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 +449,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 +470,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)

40
deps/zlib/gzwrite.c vendored
View File

@ -1,5 +1,5 @@
/* gzwrite.c -- zlib functions for writing gzip files
* Copyright (C) 2004-2017 Mark Adler
* Copyright (C) 2004-2019 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -97,6 +97,15 @@ local int gz_comp(state, flush)
return 0;
}
/* check for a pending reset */
if (state->reset) {
/* don't start a new gzip member unless there is data to write */
if (strm->avail_in == 0)
return 0;
deflateReset(strm);
state->reset = 0;
}
/* run deflate() on provided input until it produces no more output */
ret = Z_OK;
do {
@ -134,7 +143,7 @@ local int gz_comp(state, flush)
/* if that completed a deflate stream, allow another to start */
if (flush == Z_FINISH)
deflateReset(strm);
state->reset = 1;
/* all done, no errors */
return 0;
@ -209,7 +218,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 +238,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)
@ -349,12 +358,11 @@ int ZEXPORT gzputc(file, c)
}
/* -- see zlib.h -- */
int ZEXPORT gzputs(file, str)
int ZEXPORT gzputs(file, s)
gzFile file;
const char *str;
const char *s;
{
int ret;
z_size_t len;
z_size_t len, put;
gz_statep state;
/* get internal structure */
@ -367,9 +375,13 @@ int ZEXPORT gzputs(file, str)
return -1;
/* write string */
len = strlen(str);
ret = gz_write(state, str, len);
return ret == 0 && len != 0 ? -1 : ret;
len = strlen(s);
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, s, len);
return put < len ? -1 : (int)len;
}
#if defined(STDC) || defined(Z_HAVE_STDARG_H)
@ -441,7 +453,7 @@ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va)
strm->avail_in = state->size;
if (gz_comp(state, Z_NO_FLUSH) == -1)
return state->err;
memcpy(state->in, state->in + state->size, left);
memmove(state->in, state->in + state->size, left);
strm->next_in = state->in;
strm->avail_in = left;
}
@ -462,7 +474,7 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, ...)
#else /* !STDC && !Z_HAVE_STDARG_H */
/* -- see zlib.h -- */
int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
int ZEXPORTVA gzprintf(file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
gzFile file;
const char *format;
@ -540,7 +552,7 @@ int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
strm->avail_in = state->size;
if (gz_comp(state, Z_NO_FLUSH) == -1)
return state->err;
memcpy(state->in, state->in + state->size, left);
memmove(state->in, state->in + state->size, left);
strm->next_in = state->in;
strm->avail_in = left;
}

26
deps/zlib/infback.c vendored
View File

@ -1,5 +1,5 @@
/* infback.c -- inflate using a call-back interface
* Copyright (C) 1995-2016 Mark Adler
* Copyright (C) 1995-2022 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -66,6 +66,7 @@ int stream_size;
state->window = window;
state->wnext = 0;
state->whave = 0;
state->sane = 1;
return Z_OK;
}
@ -454,11 +455,11 @@ void FAR *out_desc;
}
/* build code tables -- note: do not change the lenbits or distbits
values here (9 and 6) without reading the comments in inftrees.h
values here (10 and 9) without reading the comments in inftrees.h
concerning the ENOUGH constants, which depend on those values */
state->next = state->codes;
state->lencode = (code const FAR *)(state->next);
state->lenbits = 9;
state->lenbits = 10;
ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
&(state->lenbits), state->work);
if (ret) {
@ -467,7 +468,7 @@ void FAR *out_desc;
break;
}
state->distcode = (code const FAR *)(state->next);
state->distbits = 6;
state->distbits = 9;
ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
&(state->next), &(state->distbits), state->work);
if (ret) {
@ -477,6 +478,7 @@ void FAR *out_desc;
}
Tracev((stderr, "inflate: codes ok\n"));
state->mode = LEN;
/* fallthrough */
case LEN:
/* use inflate_fast() if we have enough input and output */
@ -605,25 +607,27 @@ void FAR *out_desc;
break;
case DONE:
/* inflate stream terminated properly -- write leftover output */
/* inflate stream terminated properly */
ret = Z_STREAM_END;
if (left < state->wsize) {
if (out(out_desc, state->window, state->wsize - left))
ret = Z_BUF_ERROR;
}
goto inf_leave;
case BAD:
ret = Z_DATA_ERROR;
goto inf_leave;
default: /* can't happen, but makes compilers happy */
default:
/* can't happen, but makes compilers happy */
ret = Z_STREAM_ERROR;
goto inf_leave;
}
/* Return unused input */
/* Write leftover output and return unused input */
inf_leave:
if (left < state->wsize) {
if (out(out_desc, state->window, state->wsize - left) &&
ret == Z_STREAM_END)
ret = Z_BUF_ERROR;
}
strm->next_in = next;
strm->avail_in = have;
return ret;

28
deps/zlib/inffast.c vendored
View File

@ -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 */

60
deps/zlib/inflate.c vendored
View File

@ -1,5 +1,5 @@
/* inflate.c -- zlib decompression
* Copyright (C) 1995-2016 Mark Adler
* Copyright (C) 1995-2022 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -130,6 +130,7 @@ z_streamp strm;
state->mode = HEAD;
state->last = 0;
state->havedict = 0;
state->flags = -1;
state->dmax = 32768U;
state->head = Z_NULL;
state->hold = 0;
@ -167,6 +168,8 @@ int windowBits;
/* extract wrap request from windowBits parameter */
if (windowBits < 0) {
if (windowBits < -15)
return Z_STREAM_ERROR;
wrap = 0;
windowBits = -windowBits;
}
@ -448,10 +451,10 @@ unsigned copy;
/* check function to use adler32() for zlib or crc32() for gzip */
#ifdef GUNZIP
# define UPDATE(check, buf, len) \
# define UPDATE_CHECK(check, buf, len) \
(state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
#else
# define UPDATE(check, buf, len) adler32(check, buf, len)
# define UPDATE_CHECK(check, buf, len) adler32(check, buf, len)
#endif
/* check macros for header crc */
@ -671,7 +674,6 @@ int flush;
state->mode = FLAGS;
break;
}
state->flags = 0; /* expect zlib header */
if (state->head != Z_NULL)
state->head->done = -1;
if (!(state->wrap & 1) || /* check if zlib header allowed */
@ -698,6 +700,7 @@ int flush;
break;
}
state->dmax = 1U << len;
state->flags = 0; /* indicate zlib header */
Tracev((stderr, "inflate: zlib header ok\n"));
strm->adler = state->check = adler32(0L, Z_NULL, 0);
state->mode = hold & 0x200 ? DICTID : TYPE;
@ -723,6 +726,7 @@ int flush;
CRC2(state->check, hold);
INITBITS();
state->mode = TIME;
/* fallthrough */
case TIME:
NEEDBITS(32);
if (state->head != Z_NULL)
@ -731,6 +735,7 @@ int flush;
CRC4(state->check, hold);
INITBITS();
state->mode = OS;
/* fallthrough */
case OS:
NEEDBITS(16);
if (state->head != Z_NULL) {
@ -741,6 +746,7 @@ int flush;
CRC2(state->check, hold);
INITBITS();
state->mode = EXLEN;
/* fallthrough */
case EXLEN:
if (state->flags & 0x0400) {
NEEDBITS(16);
@ -754,14 +760,16 @@ int flush;
else if (state->head != Z_NULL)
state->head->extra = Z_NULL;
state->mode = EXTRA;
/* fallthrough */
case EXTRA:
if (state->flags & 0x0400) {
copy = state->length;
if (copy > have) copy = have;
if (copy) {
if (state->head != Z_NULL &&
state->head->extra != Z_NULL) {
len = state->head->extra_len - state->length;
state->head->extra != Z_NULL &&
(len = state->head->extra_len - state->length) <
state->head->extra_max) {
zmemcpy(state->head->extra + len, next,
len + copy > state->head->extra_max ?
state->head->extra_max - len : copy);
@ -776,6 +784,7 @@ int flush;
}
state->length = 0;
state->mode = NAME;
/* fallthrough */
case NAME:
if (state->flags & 0x0800) {
if (have == 0) goto inf_leave;
@ -797,6 +806,7 @@ int flush;
state->head->name = Z_NULL;
state->length = 0;
state->mode = COMMENT;
/* fallthrough */
case COMMENT:
if (state->flags & 0x1000) {
if (have == 0) goto inf_leave;
@ -817,6 +827,7 @@ int flush;
else if (state->head != Z_NULL)
state->head->comment = Z_NULL;
state->mode = HCRC;
/* fallthrough */
case HCRC:
if (state->flags & 0x0200) {
NEEDBITS(16);
@ -840,6 +851,7 @@ int flush;
strm->adler = state->check = ZSWAP32(hold);
INITBITS();
state->mode = DICT;
/* fallthrough */
case DICT:
if (state->havedict == 0) {
RESTORE();
@ -847,8 +859,10 @@ int flush;
}
strm->adler = state->check = adler32(0L, Z_NULL, 0);
state->mode = TYPE;
/* fallthrough */
case TYPE:
if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
/* fallthrough */
case TYPEDO:
if (state->last) {
BYTEBITS();
@ -899,8 +913,10 @@ int flush;
INITBITS();
state->mode = COPY_;
if (flush == Z_TREES) goto inf_leave;
/* fallthrough */
case COPY_:
state->mode = COPY;
/* fallthrough */
case COPY:
copy = state->length;
if (copy) {
@ -936,6 +952,7 @@ int flush;
Tracev((stderr, "inflate: table sizes ok\n"));
state->have = 0;
state->mode = LENLENS;
/* fallthrough */
case LENLENS:
while (state->have < state->ncode) {
NEEDBITS(3);
@ -957,6 +974,7 @@ int flush;
Tracev((stderr, "inflate: code lengths ok\n"));
state->have = 0;
state->mode = CODELENS;
/* fallthrough */
case CODELENS:
while (state->have < state->nlen + state->ndist) {
for (;;) {
@ -1016,11 +1034,11 @@ int flush;
}
/* build code tables -- note: do not change the lenbits or distbits
values here (9 and 6) without reading the comments in inftrees.h
values here (10 and 9) without reading the comments in inftrees.h
concerning the ENOUGH constants, which depend on those values */
state->next = state->codes;
state->lencode = (const code FAR *)(state->next);
state->lenbits = 9;
state->lenbits = 10;
ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
&(state->lenbits), state->work);
if (ret) {
@ -1029,7 +1047,7 @@ int flush;
break;
}
state->distcode = (const code FAR *)(state->next);
state->distbits = 6;
state->distbits = 9;
ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
&(state->next), &(state->distbits), state->work);
if (ret) {
@ -1040,8 +1058,10 @@ int flush;
Tracev((stderr, "inflate: codes ok\n"));
state->mode = LEN_;
if (flush == Z_TREES) goto inf_leave;
/* fallthrough */
case LEN_:
state->mode = LEN;
/* fallthrough */
case LEN:
if (have >= INFLATE_FAST_MIN_INPUT &&
left >= INFLATE_FAST_MIN_OUTPUT) {
@ -1092,6 +1112,7 @@ int flush;
}
state->extra = (unsigned)(here.op) & 15;
state->mode = LENEXT;
/* fallthrough */
case LENEXT:
if (state->extra) {
NEEDBITS(state->extra);
@ -1102,6 +1123,7 @@ int flush;
Tracevv((stderr, "inflate: length %u\n", state->length));
state->was = state->length;
state->mode = DIST;
/* fallthrough */
case DIST:
for (;;) {
here = state->distcode[BITS(state->distbits)];
@ -1129,6 +1151,7 @@ int flush;
state->offset = (unsigned)here.val;
state->extra = (unsigned)(here.op) & 15;
state->mode = DISTEXT;
/* fallthrough */
case DISTEXT:
if (state->extra) {
NEEDBITS(state->extra);
@ -1145,6 +1168,7 @@ int flush;
#endif
Tracevv((stderr, "inflate: distance %u\n", state->offset));
state->mode = MATCH;
/* fallthrough */
case MATCH:
if (left == 0) goto inf_leave;
copy = out - left;
@ -1204,7 +1228,7 @@ int flush;
state->total += out;
if ((state->wrap & 4) && out)
strm->adler = state->check =
UPDATE(state->check, put - out, out);
UPDATE_CHECK(state->check, put - out, out);
out = left;
if ((state->wrap & 4) && (
#ifdef GUNZIP
@ -1220,10 +1244,11 @@ int flush;
}
#ifdef GUNZIP
state->mode = LENGTH;
/* fallthrough */
case LENGTH:
if (state->wrap && state->flags) {
NEEDBITS(32);
if (hold != (state->total & 0xffffffffUL)) {
if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) {
strm->msg = (char *)"incorrect length check";
state->mode = BAD;
break;
@ -1233,6 +1258,7 @@ int flush;
}
#endif
state->mode = DONE;
/* fallthrough */
case DONE:
ret = Z_STREAM_END;
goto inf_leave;
@ -1242,6 +1268,7 @@ int flush;
case MEM:
return Z_MEM_ERROR;
case SYNC:
/* fallthrough */
default:
return Z_STREAM_ERROR;
}
@ -1267,7 +1294,7 @@ int flush;
state->total += out;
if ((state->wrap & 4) && out)
strm->adler = state->check =
UPDATE(state->check, strm->next_out - out, out);
UPDATE_CHECK(state->check, strm->next_out - out, out);
strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
(state->mode == TYPE ? 128 : 0) +
(state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
@ -1403,6 +1430,7 @@ int ZEXPORT inflateSync(strm)
z_streamp strm;
{
unsigned len; /* number of bytes to look at or looked at */
int flags; /* temporary to save header status */
unsigned long in, out; /* temporary to save total_in and total_out */
unsigned char buf[4]; /* to restore bit buffer to byte string */
struct inflate_state FAR *state;
@ -1435,9 +1463,15 @@ z_streamp strm;
/* return no joy or set up to restart inflate() on a new block */
if (state->have != 4) return Z_DATA_ERROR;
if (state->flags == -1)
state->wrap = 0; /* if no header yet, treat as raw */
else
state->wrap &= ~4; /* no point in computing a check value now */
flags = state->flags;
in = strm->total_in; out = strm->total_out;
inflateReset(strm);
strm->total_in = in; strm->total_out = out;
state->flags = flags;
state->mode = TYPE;
return Z_OK;
}
@ -1533,7 +1567,7 @@ int check;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
state = (struct inflate_state FAR *)strm->state;
if (check)
if (check && state->wrap)
state->wrap |= 4;
else
state->wrap &= ~4;

5
deps/zlib/inflate.h vendored
View File

@ -1,5 +1,5 @@
/* inflate.h -- internal inflate state definition
* Copyright (C) 1995-2016 Mark Adler
* Copyright (C) 1995-2019 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -86,7 +86,8 @@ struct inflate_state {
int wrap; /* bit 0 true for zlib, bit 1 true for gzip,
bit 2 true to validate check value */
int havedict; /* true if dictionary provided */
int flags; /* gzip header method and flags (0 if zlib) */
int flags; /* gzip header method and flags, 0 if zlib, or
-1 if raw or no header yet */
unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */
unsigned long check; /* protected copy of check value */
unsigned long total; /* protected copy of output count */

View File

@ -1,5 +1,5 @@
/* inftrees.c -- generate Huffman trees for efficient decoding
* Copyright (C) 1995-2017 Mark Adler
* Copyright (C) 1995-2022 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@ -9,7 +9,7 @@
#define MAXBITS 15
const char inflate_copyright[] =
" inflate 1.2.11 Copyright 1995-2017 Mark Adler ";
" inflate 1.2.13 Copyright 1995-2022 Mark Adler ";
/*
If you use the zlib library in a product, an acknowledgment is welcome
in the documentation of your product. If for some reason you cannot
@ -62,7 +62,7 @@ unsigned short FAR *work;
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
static const unsigned short lext[31] = { /* Length codes 257..285 extra */
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202};
19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 194, 65};
static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,

12
deps/zlib/inftrees.h vendored
View File

@ -36,17 +36,17 @@ typedef struct {
*/
/* Maximum size of the dynamic table. The maximum number of code structures is
1444, which is the sum of 852 for literal/length codes and 592 for distance
1924, which is the sum of 1332 for literal/length codes and 592 for distance
codes. These values were found by exhaustive searches using the program
examples/enough.c found in the zlib distribtution. The arguments to that
examples/enough.c found in the zlib distribution. The arguments to that
program are the number of symbols, the initial root table size, and the
maximum bit length of a code. "enough 286 9 15" for literal/length codes
returns returns 852, and "enough 30 6 15" for distance codes returns 592.
The initial root table size (9 or 6) is found in the fifth argument of the
maximum bit length of a code. "enough 286 10 15" for literal/length codes
returns returns 1332, and "enough 30 9 15" for distance codes returns 592.
The initial root table size (10 or 9) is found in the fifth argument of the
inflate_table() calls in inflate.c and infback.c. If the root table size is
changed, then these maximum sizes would be need to be recalculated and
updated. */
#define ENOUGH_LENS 852
#define ENOUGH_LENS 1332
#define ENOUGH_DISTS 592
#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)

View File

@ -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 */

22
deps/zlib/patches/0004-fix-uwp.patch vendored Normal file
View File

@ -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));

View File

@ -0,0 +1,405 @@
From 409594639f15d825202971db7a275023e05772ff Mon Sep 17 00:00:00 2001
From: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
+
#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 <iostream>
#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<struct mem_zone *>(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<struct mem_item *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<struct mem_zone *>(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<unsigned char *>(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<unsigned char *>(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<unsigned char *>(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(&copy, &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<struct inflate_state *>(((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<unsigned char *>(malloc(size));
assert(out != NULL);
- win = malloc(32768);
+ win = static_cast<unsigned char *>(malloc(32768));
assert(win != NULL);
- prefix = malloc(strlen(id) + 6);
+ prefix = static_cast<char *>(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)

View File

@ -0,0 +1,42 @@
From 8304bdda5293ffd5b3efce8e4f54904b387029d6 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans@chromium.org>
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

View File

@ -0,0 +1,40 @@
From 92537ee19784e0e545f06d89b7d89ab532a18cff Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans@chromium.org>
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

View File

@ -0,0 +1,98 @@
From 0c7de17000659f4f79de878296892c46be0aff77 Mon Sep 17 00:00:00 2001
From: Noel Gordon <noel@chromium.org>
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 <time.h>
#include <errno.h>
#include <fcntl.h>
+#include <sys/stat.h>
#ifdef _WIN32
# include <direct.h>
@@ -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

View File

@ -0,0 +1,24 @@
From 75690b2683667be5535ac6243438115dc9c40f6a Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer@google.com>
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");

Some files were not shown because too many files have changed in this diff Show More