undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,138 @@
# Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# @test
# @bug 4262583 4418997 4795136
# @summary Check support for jar file members with sizes > 2GB
# @author Martin Buchholz
#
# @build FileBuilder
# @run shell 3GBZipFiles.sh 9986
# @ignore runs for hours and eats up 7 Gigabytes of disk space
# @run shell/timeout=604800 3GBZipFiles.sh 3141592653
# @key randomness
# Command-line usage:
# javac FileBuilder.java && sh 3GBZipFiles.sh /path/to/jdk filesize
# -------------------------------------------------------------------
# Testing strategy: We want to test for size limits on the Jar file
# itself, as well as on the compressed and uncompressed sizes of the
# files stored therein. All of these limits should be 4GB and should
# be tested in the 2GB-4GB range. We also want to check that it is
# possible to store more than 6GB of actual data in a zip file, if we
# have two files of size 3GB which compress nicely. We also want to
# test both the "STORED" and "DEFLATED" compression methods.
# -------------------------------------------------------------------
die () { echo "$1" >&2; exit 1; }
sys () { "$@" || die "Command $@ failed: rc=$?"; }
set -u
myName=`printf %s "$0" | sed 's:.*[/\\]::'`;
if test -z "${TESTJAVA-}"; then
test "$#" -eq 2 || die "Usage: $myName /path/to/jdk filesize"
TESTJAVA="$1"; shift
TESTCLASSES="`pwd`"
fi
hugeSize="$1"; shift
tinySize=42
JAVA="$TESTJAVA/bin/java"
JAR="$TESTJAVA/bin/jar"
currentDir="`pwd`"
tmpDir="$myName.tmp"
cleanup () { cd "$currentDir" && rm -rf "$tmpDir"; }
trap cleanup 0 1 2 15
sys rm -rf "$tmpDir"
sys mkdir "$tmpDir"
cd "$tmpDir"
buildFile ()
{
filetype_="$1"
filename_="$2"
case "$filename_" in
huge-*) filesize_="$hugeSize" ;;
tiny-*) filesize_="$tinySize" ;;
esac
sys "$JAVA" ${TESTVMOPTS} "-cp" "$TESTCLASSES" "FileBuilder" \
"$filetype_" "$filename_" "$filesize_"
}
testJarFile ()
{
echo "-------------------------------------------------------"
echo "Testing $1 $2"
echo "-------------------------------------------------------"
filetype="$1"
if test "$2" = "STORED"; then jarOpt="0"; else jarOpt=""; fi
filelist="$3"
jarFile="$myName.jar"
for file in $filelist; do
buildFile "$filetype" "$file"
done
sys "$JAR" cvM${jarOpt}f "$jarFile" $filelist
sys ls -l "$jarFile"
sys "$JAR" tvf "$jarFile"
for file in $filelist; do
case "$file" in
huge-*) size="$hugeSize" ;;
tiny-*) size="$tinySize" ;;
esac
case "`$JAR tvf $jarFile $file`" in
*"$size"*"$file"*) : ;;
*) die "Output of \"jar tvf\" is incorrect." ;;
esac
# Try to minimize disk space used while verifying the jar file.
sum1="`sum $file`"
sys rm "$file"
sys "$JAR" xvf "$jarFile" "$file"
sum2="`sum $file`"
test "$sum1" = "$sum2" || die "Jar File is corrupted."
sys rm "$file"
# unzip $jarFile $file
# sys rm "$file"
done
sys rm "$jarFile"
}
testJarFile "MostlyEmpty" "DEFLATED" "tiny-1 huge-1 tiny-2 huge-2 tiny-3"
testJarFile "MostlyEmpty" "STORED" "tiny-1 huge-1 tiny-2"
testJarFile "SlightlyCompressible" "DEFLATED" "tiny-1 huge-1 tiny-2"
cleanup
exit 0

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4028605 4109069 4234207 4401122 8339154
* @summary Verify that ZipInputStream, InflaterInputStream, ZipFileInputStream,
* ZipFileInflaterInputStream.available() return values according
* to their specification or long-standing behavior
* @run junit Available
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class Available {
// ZIP file produced in this test
private final Path zip = Path.of("available.jar");
/**
* Create the ZIP file used in this test, containing
* one deflated and one stored entry.
*
* @throws IOException if an unexpected error occurs
*/
@BeforeEach
public void setup() throws IOException {
byte[] contents = "contents".repeat(10).getBytes(StandardCharsets.UTF_8);
try (ZipOutputStream zo = new ZipOutputStream(Files.newOutputStream(zip))) {
// First entry uses DEFLATE method
zo.putNextEntry(new ZipEntry("deflated.txt"));
zo.write(contents);
// Second entry uses STORED method
ZipEntry stored = new ZipEntry("stored.txt");
stored.setMethod(ZipEntry.STORED);
stored.setSize(contents.length);
CRC32 crc32 = new CRC32();
crc32.update(contents);
stored.setCrc(crc32.getValue());
zo.putNextEntry(stored);
zo.write(contents);
}
}
/**
* Delete the ZIP file created by this test
*
* @throws IOException if an unexpected error occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/**
* Verify that ZipInputStream.available() returns 0 after EOF or
* closeEntry, otherwise 1, as specified in the API description.
* This tests 4028605 4109069 4234207
* @throws IOException if an unexpected error occurs
*/
@Test
public void testZipInputStream() throws IOException {
try (InputStream in = Files.newInputStream(zip)) {
ZipInputStream z = new ZipInputStream(in);
z.getNextEntry();
assertEquals(1, z.available());
z.read();
assertEquals(1, z.available());
z.transferTo(OutputStream.nullOutputStream());
assertEquals(0, z.available(),
"ZipInputStream.available() should return 0 after EOF");
z.close();
assertThrows(IOException.class, () -> z.available(),
"Expected an IOException when calling available on a closed stream");
}
try (InputStream in = Files.newInputStream(zip);
ZipInputStream z = new ZipInputStream(in)) {
z.getNextEntry();
z.closeEntry();
assertEquals(0, z.available(),
"ZipInputStream.available() should return 0 after closeEntry");
}
}
/**
* Verify that ZipFileInputStream|ZipFileInflaterInputStream.available()
* return the number of remaining uncompressed bytes.
*
* This verifies unspecified, but long-standing behavior. See 4401122.
*
* @throws IOException if an unexpected error occurs
*/
@ParameterizedTest
@ValueSource(strings = { "stored.txt", "deflated.txt" })
public void testZipFileStreamsRemainingBytes(String entryName) throws IOException {
try (ZipFile zfile = new ZipFile(zip.toFile())) {
ZipEntry entry = zfile.getEntry(entryName);
// Could be ZipFileInputStream or ZipFileInflaterInputStream
InputStream in = zfile.getInputStream(entry);
int initialAvailable = in.available();
// Initally, the number of remaining uncompressed bytes is the entry size
assertEquals(entry.getSize(), initialAvailable);
// Read all bytes one by one
for (int i = initialAvailable; i > 0; i--) {
// Reading a single byte should decrement available by 1
in.read();
assertEquals(i - 1, in.available(), "Available not decremented");
}
// No remaining uncompressed bytes
assertEquals(0, in.available());
// available() should still return 0 after close
in.close();
assertEquals(0, in.available());
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4811913 6894950
* @summary Test bounds checking in zip package
*/
import java.util.zip.*;
public class Bounds {
public static void main(String[] args) throws Exception {
byte[] b = new byte[0];
int offset = 4;
int length = Integer.MAX_VALUE - 3;
try {
(new CRC32()).update(b, offset, length);
throw new RuntimeException("Expected exception not thrown");
} catch (ArrayIndexOutOfBoundsException aioobe) {
// Correct result
}
try {
(new Deflater()).setDictionary(b, offset, length);
throw new RuntimeException("Expected exception not thrown");
} catch (ArrayIndexOutOfBoundsException aioobe) {
// Correct result
}
try {
(new Adler32()).update(b, offset, length);
throw new RuntimeException("Expected exception not thrown");
} catch (ArrayIndexOutOfBoundsException aioobe) {
// Correct result
}
try {
(new Deflater()).deflate(b, offset, length);
throw new RuntimeException("Expected exception not thrown");
} catch (ArrayIndexOutOfBoundsException aioobe) {
// Correct result
}
try {
(new Inflater()).inflate(b, offset, length);
throw new RuntimeException("Expected exception not thrown");
} catch (ArrayIndexOutOfBoundsException aioobe) {
// Correct result
}
}
}

View file

@ -0,0 +1,205 @@
/*
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.foreign.MemorySegment;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.zip.Checksum;
/**
* Base class for Checksum tests
*/
public class ChecksumBase {
private static final byte[] BYTES_123456789 = "123456789".getBytes(StandardCharsets.US_ASCII);
public static void testAll(Checksum checksum, long expected) {
testBytes(checksum, expected);
testByteArray(checksum, expected);
testWrappedMemorySegment(checksum, expected);
testWrappedByteBuffer(checksum, expected);
testReadonlyByteBuffer(checksum, expected);
testDirectByteBuffer(checksum, expected);
testByteArrayOffset(checksum, expected);
testDirectByteBufferOffset(checksum, expected);
testLittleEndianDirectByteBufferOffset(checksum, expected);
testWrappedByteBufferOffset(checksum, expected);
testLittleEndianWrappedByteBufferOffset(checksum, expected);
testReadonlyByteBufferOffset(checksum, expected);
testLittleEndianReadonlyByteBufferOffset(checksum, expected);
}
private static void testBytes(Checksum checksum, long expected) {
checksum.reset();
for (byte bits : BYTES_123456789) {
checksum.update(bits);
}
checkChecksum(checksum, expected);
}
private static void testByteArray(Checksum checksum, long expected) {
checksum.reset();
checksum.update(BYTES_123456789);
checkChecksum(checksum, expected);
}
private static void testWrappedByteBuffer(Checksum checksum, long expected) {
checksum.reset();
ByteBuffer bb = ByteBuffer.wrap(BYTES_123456789);
checksum.update(bb);
checkChecksum(checksum, expected);
}
private static void testWrappedMemorySegment(Checksum checksum, long expected) {
checksum.reset();
ByteBuffer bb = MemorySegment.ofArray(BYTES_123456789).asByteBuffer();
checksum.update(bb);
checkChecksum(checksum, expected);
}
private static void testReadonlyByteBuffer(Checksum checksum, long expected) {
checksum.reset();
ByteBuffer bb = ByteBuffer.wrap(BYTES_123456789).asReadOnlyBuffer();
checksum.update(bb);
checkChecksum(checksum, expected);
}
private static void testDirectByteBuffer(Checksum checksum, long expected) {
checksum.reset();
ByteBuffer bb = ByteBuffer.allocateDirect(BYTES_123456789.length);
bb.put(BYTES_123456789);
bb.rewind();
checksum.update(bb);
checkChecksum(checksum, expected);
}
private static void checkChecksum(Checksum checksum, long expected) {
if (checksum.getValue() != expected) {
throw new RuntimeException("Calculated checksum result was invalid."
+ " Expected " + Long.toHexString(expected)
+ ", but got " + Long.toHexString(checksum.getValue()) + ".");
}
}
private static void testByteArrayOffset(Checksum checksum, long expected) {
byte[] unaligned_bytes_123456789 = new byte[BYTES_123456789.length + 64];
for (int i = 0; i < unaligned_bytes_123456789.length - BYTES_123456789.length; i++) {
checksum.reset();
System.arraycopy(BYTES_123456789, 0, unaligned_bytes_123456789, i, BYTES_123456789.length);
checksum.update(unaligned_bytes_123456789, i, BYTES_123456789.length);
checkChecksumOffset(checksum, expected, i);
}
}
private static void testDirectByteBufferOffset(Checksum checksum, long expected) {
byte[] unaligned_bytes_123456789 = new byte[BYTES_123456789.length + 64];
for (int i = 0; i < unaligned_bytes_123456789.length - BYTES_123456789.length; i++) {
checksum.reset();
ByteBuffer bb = ByteBuffer.allocateDirect(unaligned_bytes_123456789.length);
System.arraycopy(BYTES_123456789, 0, unaligned_bytes_123456789, i, BYTES_123456789.length);
bb.put(unaligned_bytes_123456789);
bb.position(i);
bb.limit(i + BYTES_123456789.length);
checksum.update(bb);
checkChecksumOffset(checksum, expected, i);
}
}
private static void testLittleEndianDirectByteBufferOffset(Checksum checksum, long expected) {
byte[] unaligned_bytes_123456789 = new byte[BYTES_123456789.length + 64];
for (int i = 0; i < unaligned_bytes_123456789.length - BYTES_123456789.length; i++) {
checksum.reset();
ByteBuffer bb = ByteBuffer.allocateDirect(unaligned_bytes_123456789.length);
bb.order(ByteOrder.LITTLE_ENDIAN);
System.arraycopy(BYTES_123456789, 0, unaligned_bytes_123456789, i, BYTES_123456789.length);
bb.put(unaligned_bytes_123456789);
bb.position(i);
bb.limit(i + BYTES_123456789.length);
checksum.update(bb);
checkChecksumOffset(checksum, expected, i);
}
}
private static void testWrappedByteBufferOffset(Checksum checksum, long expected) {
byte[] unaligned_bytes_123456789 = new byte[BYTES_123456789.length + 64];
for (int i = 0; i < unaligned_bytes_123456789.length - BYTES_123456789.length; i++) {
checksum.reset();
System.arraycopy(BYTES_123456789, 0, unaligned_bytes_123456789, i, BYTES_123456789.length);
ByteBuffer bb = ByteBuffer.wrap(unaligned_bytes_123456789);
bb.position(i);
bb.limit(i + BYTES_123456789.length);
checksum.update(bb);
checkChecksumOffset(checksum, expected, i);
}
}
private static void testLittleEndianWrappedByteBufferOffset(Checksum checksum, long expected) {
byte[] unaligned_bytes_123456789 = new byte[BYTES_123456789.length + 64];
for (int i = 0; i < unaligned_bytes_123456789.length - BYTES_123456789.length; i++) {
checksum.reset();
System.arraycopy(BYTES_123456789, 0, unaligned_bytes_123456789, i, BYTES_123456789.length);
ByteBuffer bb = ByteBuffer.wrap(unaligned_bytes_123456789);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.position(i);
bb.limit(i + BYTES_123456789.length);
checksum.update(bb);
checkChecksumOffset(checksum, expected, i);
}
}
private static void testReadonlyByteBufferOffset(Checksum checksum, long expected) {
byte[] unaligned_bytes_123456789 = new byte[BYTES_123456789.length + 64];
for (int i = 0; i < unaligned_bytes_123456789.length - BYTES_123456789.length; i++) {
checksum.reset();
System.arraycopy(BYTES_123456789, 0, unaligned_bytes_123456789, i, BYTES_123456789.length);
ByteBuffer bb = ByteBuffer.wrap(unaligned_bytes_123456789).asReadOnlyBuffer();
bb.position(i);
bb.limit(i + BYTES_123456789.length);
checksum.update(bb);
checkChecksumOffset(checksum, expected, i);
}
}
private static void testLittleEndianReadonlyByteBufferOffset(Checksum checksum, long expected) {
byte[] unaligned_bytes_123456789 = new byte[BYTES_123456789.length + 64];
for (int i = 0; i < unaligned_bytes_123456789.length - BYTES_123456789.length; i++) {
checksum.reset();
System.arraycopy(BYTES_123456789, 0, unaligned_bytes_123456789, i, BYTES_123456789.length);
ByteBuffer bb = ByteBuffer.wrap(unaligned_bytes_123456789).asReadOnlyBuffer();
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.position(i);
bb.limit(i + BYTES_123456789.length);
checksum.update(bb);
checkChecksumOffset(checksum, expected, i);
}
}
private static void checkChecksumOffset(Checksum checksum, long expected, int offset) {
if (checksum.getValue() != expected) {
throw new RuntimeException("Calculated CRC32C result was invalid. Array offset "
+ offset + ". Expected: " + Long.toHexString(expected) + ", Got: "
+ Long.toHexString(checksum.getValue()));
}
}
}

View file

@ -0,0 +1,212 @@
/*
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8193682 8278794 8284771
* @summary Test Infinite loop while writing on closed Deflater and Inflater.
* @run junit CloseInflaterDeflaterTest
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
import java.util.jar.JarOutputStream;
import java.util.stream.Stream;
import java.util.zip.DeflaterInputStream;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.InflaterOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class CloseInflaterDeflaterTest {
// Number of bytes to write/read from Deflater/Inflater
private static final int INPUT_LENGTH= 512;
// OutputStream that will throw an exception during a write operation
private static OutputStream outStream = new OutputStream() {
@Override
public void write(byte[] b, int off, int len) throws IOException {
throw new IOException();
}
@Override
public void write(byte[] b) throws IOException {}
@Override
public void write(int b) throws IOException {}
};
// InputStream that will throw an exception during a read operation
private static InputStream inStream = new InputStream() {
@Override
public int read(byte[] b, int off, int len) throws IOException {
throw new IOException();
}
@Override
public int read(byte[] b) throws IOException { throw new IOException();}
@Override
public int read() throws IOException { throw new IOException();}
};
// Input bytes for read/write operation
private static byte[] inputBytes = new byte[INPUT_LENGTH];
// Random function to add bytes to inputBytes
private static Random rand = new Random();
/**
* MethodSource to specify whether to use close() or finish() of OutputStream
*
* @return Stream indicating which method to use for closing OutputStream
*/
public static Stream<Boolean> testOutputStreams() {
return Stream.of(true, false);
}
/**
* MethodSource to specify on which outputstream closeEntry() has to be called
*
* @return Stream consisting of either a JarOutputStream or ZipOutputStream
*/
public static Stream<ZipOutputStream> testZipAndJar() throws IOException{
return Stream.of(new JarOutputStream(outStream), new ZipOutputStream(outStream));
}
/**
* Add inputBytes array with random bytes to write into OutputStream
*/
@BeforeAll
public static void before_test()
{
rand.nextBytes(inputBytes);
}
/**
* Test for infinite loop by writing bytes to closed GZIPOutputStream
*
* @param useCloseMethod indicates whether to use Close() or finish() method
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("testOutputStreams")
public void testGZip(boolean useCloseMethod) throws IOException {
GZIPOutputStream gzip = new GZIPOutputStream(outStream);
gzip.write(inputBytes, 0, INPUT_LENGTH);
assertThrows(IOException.class, () -> {
// Close GZIPOutputStream
if (useCloseMethod) {
gzip.close();
} else {
gzip.finish();
}
});
// Write on a closed GZIPOutputStream, closed Deflater IllegalStateException expected
assertThrows(IllegalStateException.class , () -> gzip.write(inputBytes, 0, INPUT_LENGTH));
}
/**
* Test for infinite loop by writing bytes to closed DeflaterOutputStream
*
* @param useCloseMethod indicates whether to use Close() or finish() method
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("testOutputStreams")
public void testDeflaterOutputStream(boolean useCloseMethod) throws IOException {
DeflaterOutputStream def = new DeflaterOutputStream(outStream);
assertThrows(IOException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH));
assertThrows(IOException.class, () -> {
// Close DeflaterOutputStream
if (useCloseMethod) {
def.close();
} else {
def.finish();
}
});
// Write on a closed DeflaterOutputStream, IllegalStateException is expected
assertThrows(IllegalStateException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH));
}
/**
* Test for infinite loop by reading bytes from closed DeflaterInputStream
*
* @throws IOException if an error occurs
*/
@Test
public void testDeflaterInputStream() throws IOException {
DeflaterInputStream def = new DeflaterInputStream(inStream);
assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH));
// Close DeflaterInputStream
def.close();
// Read from a closed DeflaterInputStream, closed Deflater IOException expected
assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH));
}
/**
* Test for infinite loop by writing bytes to closed InflaterOutputStream
*
* Note: Disabling this test as it is failing intermittently.
* @param useCloseMethod indicates whether to use Close() or finish() method
* @throws IOException if an error occurs
*/
@Disabled
@ParameterizedTest
@MethodSource("testOutputStreams")
public void testInflaterOutputStream(boolean useCloseMethod) throws IOException {
InflaterOutputStream inf = new InflaterOutputStream(outStream);
assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH));
assertThrows(IOException.class , () -> {
// Close InflaterOutputStream
if (useCloseMethod) {
inf.close();
} else {
inf.finish();
}
});
// Write on a closed InflaterOutputStream , closed Inflater IOException expected
assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH));
}
/**
* Test for infinite loop by writing bytes to closed ZipOutputStream/JarOutputStream
*
* @param zip will be the instance of either JarOutputStream or ZipOutputStream
* @throws IOException if an error occurs
*/
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("testZipAndJar")
public void testZipCloseEntry(ZipOutputStream zip) throws IOException {
assertThrows(IOException.class , () -> zip.putNextEntry(new ZipEntry("")));
zip.write(inputBytes, 0, INPUT_LENGTH);
assertThrows(IOException.class , () -> zip.closeEntry());
// Write on a closed ZipOutputStream , IllegalStateException is expected
assertThrows(IllegalStateException.class , () -> zip.write(inputBytes, 0, INPUT_LENGTH));
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4679743 8148624
* @summary Test parts of DeflaterInputStream code that don't really do I/O.
*/
import java.io.*;
import java.util.zip.*;
public class ConstructDeflaterInput {
static class MyDeflater extends Deflater {
volatile boolean ended = false;
public void end() {
ended = true;
super.end();
}
}
public static void realMain(String[] args) throws Throwable {
final MyDeflater def = new MyDeflater();
ByteArrayInputStream bais = new ByteArrayInputStream(
"hello, world".getBytes());
DeflaterInputStream dis = null;
byte[] b = new byte[512];
// Check construction
//
try {
dis = new DeflaterInputStream(null);
fail();
} catch (NullPointerException ex) {
pass();
}
try {
dis = new DeflaterInputStream(bais, null);
fail();
} catch (NullPointerException ex) {
pass();
}
try {
dis = new DeflaterInputStream(bais, def, 0);
fail();
} catch (IllegalArgumentException ex) {
pass();
}
// Check sanity checks in read methods
//
dis = new DeflaterInputStream(bais, def);
try {
dis.read(null, 5, 2);
fail();
} catch (NullPointerException ex) {
pass();
}
try {
dis.read(b, -1, 0);
fail();
} catch (IndexOutOfBoundsException ex) {
pass();
}
try {
dis.read(b, 0, -1);
fail();
} catch (IndexOutOfBoundsException ex) {
pass();
}
try {
dis.read(b, 0, 600);
fail();
} catch (IndexOutOfBoundsException ex) {
pass();
}
int len = 0;
try {
len = dis.read(b, 0, 0);
check(len == 0);
} catch (IndexOutOfBoundsException ex) {
fail("Read of length 0 should return 0, but returned " + len);
}
try {
dis.skip(-1);
fail();
} catch (IllegalArgumentException ex) {
pass();
}
// Check unsupported operations
//
check(!dis.markSupported());
check(dis.available() == 1);
check(!def.ended);
try {
dis.reset();
fail();
} catch (IOException ex) {
pass();
}
// Check close
//
dis.close();
check(!def.ended);
try {
dis.available();
fail();
} catch (IOException ex) {
pass();
}
try {
int x = dis.read();
fail();
} catch (IOException ex) {
pass();
}
try {
dis.skip(1);
fail();
} catch (IOException ex) {
pass();
}
java.lang.ref.Reference.reachabilityFence(def);
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4679743 8148624
* @summary Test parts of InflaterOutputStream code that don't really do I/O.
*/
import java.io.*;
import java.util.zip.*;
public class ConstructInflaterOutput {
static class MyInflater extends Inflater {
volatile boolean ended = false;
public void end() {
ended = true;
super.end();
}
}
public static void realMain(String[] args) throws Throwable {
final MyInflater inf = new MyInflater();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InflaterOutputStream ios = null;
byte[] b = new byte[512];
// Check construction
//
try {
ios = new InflaterOutputStream(null);
fail();
} catch (NullPointerException ex) {
pass();
}
try {
ios = new InflaterOutputStream(baos, null);
fail();
} catch (NullPointerException ex) {
pass();
}
try {
ios = new InflaterOutputStream(baos, inf, 0);
fail();
} catch (IllegalArgumentException ex) {
pass();
}
// Check sanity checks in write methods
//
ios = new InflaterOutputStream(baos, inf);
try {
ios.write(null, 5, 2);
fail();
} catch (NullPointerException ex) {
pass();
}
try {
ios.write(b, -1, 0);
fail();
} catch (IndexOutOfBoundsException ex) {
pass();
}
try {
ios.write(b, 0, -1);
fail();
} catch (IndexOutOfBoundsException ex) {
pass();
}
try {
ios.write(b, 0, 600);
fail();
} catch (IndexOutOfBoundsException ex) {
pass();
}
ios.flush();
check(!inf.ended);
ios.flush();
check(!inf.ended);
ios.finish();
check(!inf.ended);
ios.close();
check(!inf.ended);
try {
ios.finish();
fail();
} catch (IOException ex) {
pass();
}
try {
ios.write(13);
fail();
} catch (IOException ex) {
pass();
}
ios = new InflaterOutputStream(baos);
ios.flush();
ios.finish();
ios.close();
try {
ios.flush();
} catch (IOException ex) {
pass();
}
java.lang.ref.Reference.reachabilityFence(inf);
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,268 @@
/*
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8253952
* @summary Test behaviour when copying ZipEntries between zip files.
* @run junit CopyZipFile
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.*;
public class CopyZipFile {
// ZIP file created in this test
private Path zip = Path.of("first.zip");
// The content to put in each entry
private static final byte[] TEST_STRING = "TestTestTest".getBytes(StandardCharsets.UTF_8);
/**
* Create the sample ZIP file used in this test, including a STORED entry
* and DEFLATE entries with various compression levels.
* @throws IOException if an unexpected IOException occurs
*/
@BeforeEach
public void createZip() throws IOException {
// By default, ZipOutputStream creates zip files with Local File Headers
// without size, compressed size and crc values and an extra Data
// Descriptor (see https://en.wikipedia.org/wiki/Zip_(file_format)
// after the data belonging to that entry with these values if in the
// corresponding ZipEntry one of the size, compressedSize or crc fields is
// equal to '-1' (which is the default for newly created ZipEntries).
try (OutputStream os = Files.newOutputStream(zip) ;
ZipOutputStream zos = new ZipOutputStream(os)) {
// First file will be compressed with DEFAULT_COMPRESSION (i.e. -1 or 6)
zos.setLevel(Deflater.DEFAULT_COMPRESSION);
zos.putNextEntry(new ZipEntry("DEFAULT_COMPRESSION.txt"));
zos.write(TEST_STRING);
// Second file won't be compressed at all (i.e. STORED)
zos.setMethod(ZipOutputStream.STORED);
ZipEntry ze = new ZipEntry("STORED.txt");
ze.setSize(TEST_STRING.length);
ze.setCompressedSize(TEST_STRING.length);
CRC32 crc = new CRC32();
crc.update(TEST_STRING);
ze.setCrc(crc.getValue());
zos.putNextEntry(ze);
zos.write(TEST_STRING);
// Third file will be compressed with NO_COMPRESSION (i.e. 0)
zos.setMethod(ZipOutputStream.DEFLATED);
zos.setLevel(Deflater.NO_COMPRESSION);
zos.putNextEntry(new ZipEntry("NO_COMPRESSION.txt"));
zos.write(TEST_STRING);
// Fourth file will be compressed with BEST_SPEED (i.e. 1)
zos.setLevel(Deflater.BEST_SPEED);
zos.putNextEntry(new ZipEntry("BEST_SPEED.txt"));
zos.write(TEST_STRING);
// Fifth file will be compressed with BEST_COMPRESSION (i.e. 9)
zos.setLevel(Deflater.BEST_COMPRESSION);
zos.putNextEntry(new ZipEntry("BEST_COMPRESSION.txt"));
zos.write(TEST_STRING);
}
}
/**
* Delete the ZIP file produced by this test
* @throws IOException if an unexpected IOException occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/**
* Read all entries using ZipInputStream.getNextEntry and copy them
* to a new zip file using ZipOutputStream.putNextEntry. This only works
* reliably because the input zip file has no values for the size, compressedSize
* and crc values of streamed zip entries in the local file header and
* therefore the ZipEntry objects created by ZipOutputStream.getNextEntry
* will have all these fields set to '-1'.
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void copyFromZipInputStreamToZipOutputStream() throws IOException {
try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(zip));
ZipOutputStream zos = new ZipOutputStream(OutputStream.nullOutputStream())) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// ZipInputStream.getNextEntry() only reads the Local File Header of a zip entry,
// so for the zip file we've just generated the ZipEntry fields 'size', 'compressedSize`
// and 'crc' for deflated entries should be uninitialized (i.e. '-1').
System.out.println(
String.format("name=%s, clen=%d, len=%d, crc=%d",
entry.getName(), entry.getCompressedSize(), entry.getSize(), entry.getCrc()));
if (entry.getMethod() == ZipEntry.DEFLATED) {
// Expect size, compressed size and crc to not be initialized at this point
assertEquals(-1, entry.getCompressedSize());
assertEquals(-1, entry.getSize());
assertEquals(-1, entry.getCrc());
}
zos.putNextEntry(entry);
zis.transferTo(zos);
// After all the data belonging to a zip entry has been inflated (i.e. after ZipInputStream.read()
// returned '-1'), it is guaranteed that the ZipInputStream will also have consumed the Data
// Descriptor (if any) after the data and will have updated the 'size', 'compressedSize' and 'crc'
// fields of the ZipEntry object.
System.out.println(
String.format("name=%s, clen=%d, len=%d, crc=%d\n",
entry.getName(), entry.getCompressedSize(), entry.getSize(), entry.getCrc()));
// Expect size, compressed size and crc to be initialized at this point
assertNotEquals(-1, entry.getCompressedSize());
assertNotEquals(-1, entry.getSize());
assertNotEquals(-1, entry.getCrc());
}
}
}
/**
* Read all entries using the ZipFile class and copy them to a new zip file
* using ZipOutputStream.putNextEntry.
* The ZipFile class reads all the zip entries from the Central
* Directory, which has accurate information for size, compressedSize and crc.
* This means that all ZipEntry objects returned from ZipFile will have correct
* settings for these fields.
* If the compression level was different in the input zip file (which we can't know
* because the zip file format doesn't record this information), the
* size of the re-compressed entry we are writing to the ZipOutputStream might differ
* from the original compressed size recorded in the ZipEntry. This would result in an
* "invalid entry compressed size" ZipException if ZipOutputStream wouldn't ignore
* the implicitely set compressed size attribute of ZipEntries read from a ZipFile
* or ZipInputStream.
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void copyFromZipFileToZipOutputStream() throws IOException {
try (ZipOutputStream zos = new ZipOutputStream(OutputStream.nullOutputStream());
ZipFile zf = new ZipFile(zip.toFile())) {
ZipEntry entry;
Enumeration<? extends ZipEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
entry = entries.nextElement();
System.out.println(
String.format("name=%s, clen=%d, len=%d, crc=%d\n",
entry.getName(), entry.getCompressedSize(),
entry.getSize(), entry.getCrc()));
// Expect size, compressed size and crc to be initialized at this point
assertNotEquals(-1, entry.getCompressedSize());
assertNotEquals(-1, entry.getSize());
assertNotEquals(-1, entry.getCrc());
zos.putNextEntry(entry);
try (InputStream is = zf.getInputStream(entry)) {
is.transferTo(zos);
}
zos.closeEntry();
}
}
}
/**
* If the compressed size is set explicitly using ZipEntry.setCompressedSize(),
* then the entry will be restreamed with a data descriptor and the compressed size
* recomputed. If the source compression level was different from the target compression
* level, the compressed sizes may differ and a ZipException will be thrown
* when the entry is closed in ZipOutputStream.closeEntry
*
* @throws IOException if an unexpected IOException is thrown
*/
@Test
public void explicitCompressedSizeWithDifferentCompressionLevels() throws IOException {
try (ZipOutputStream zos = new ZipOutputStream(OutputStream.nullOutputStream());
ZipFile zf = new ZipFile(zip.toFile())) {
// Be explicit about the default compression level
zos.setLevel(Deflater.DEFAULT_COMPRESSION);
Enumeration<? extends ZipEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
// Explicitly setting the compressed size will disable data descriptors
// and enable validation that the compressed size in the ZipEntry matches the
// actual compressed size written by ZipOutputStream
entry.setCompressedSize(entry.getCompressedSize());
try (InputStream is = zf.getInputStream(entry)) {
zos.putNextEntry(entry);
is.transferTo(zos);
// Some compression levels lead to unexpected recompressed sizes when closing the entry
switch (entry.getName()) {
case "DEFAULT_COMPRESSION.txt" -> {
// DEFAULT_COMPRESSION matches expected size
zos.closeEntry();
}
case "STORED.txt" -> {
// STORED should not throw
zos.closeEntry();
}
case "NO_COMPRESSION.txt", "BEST_SPEED.txt" -> {
// NO_COMPRESSION and BEST_SPEED should lead to an unexpected recompressed size
ZipException ze = assertThrows(ZipException.class, () -> {
zos.closeEntry();
});
// Hack to fix and close the offending zip entry with the correct recompressed size.
// The exception message is something like:
// "invalid entry compressed size (expected 12 but got 7 bytes)"
// and we need to extract the second integer.
Pattern cSize = Pattern.compile("\\d+");
Matcher m = cSize.matcher(ze.getMessage());
m.find();
m.find();
entry.setCompressedSize(Integer.parseInt(m.group()));
zos.closeEntry();
}
case "BEST_COMPRESSION.txt" -> {
// BEST_COMPRESSION produces the same compressed
// size as DEFAULT_COMPRESSION for sample content
zos.closeEntry();
}
default -> {
throw new IllegalArgumentException("Unexpected entry " + entry.getName());
}
}
}
}
}
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 6334171
* @summary Test that zip file's data descriptor is written correctly.
*/
import java.io.*;
import java.util.*;
import java.util.zip.*;
/**
* Bug 6252735 ZipEntry contains wasteful "temporary storage" fields introduced
* a regression. The value of the general purpose flag bit in a LOC header is
* written incorrectly, because it is computed on stale data.
* <p>
* With the bug present, zipbytes2 is written incorrectly: when the LOC
* header is written, (flag(e) & 8) == 8. This is correct: the data will be
* compressed, so we don't have data length, etc. yet; that should be written
* in the DataDescriptor after the data itself is written. However, when the
* ZipOutputStream that wraps zipbytes2 is closed, the data length _is_
* available, therefore (flag(e) & 8) = 0), therefore the DataDescriptor is
* not written. This is why, with the bug, zipbytes1.length == sizeof(ext
* header) + zipbytes2.length.
* <p>
* The result is an invalid LOC header in zipbytes2. So when we again use
* copyZip, we attempt to read a data length not from the LOC header but from
* the non-existent EXT header, and at that position in the file is some
* arbitrary and incorrect value.
*/
public class DataDescriptor {
static void copyZip(ZipInputStream in, ZipOutputStream out) throws IOException {
byte[] buffer = new byte[1 << 14];
for (ZipEntry ze; (ze = in.getNextEntry()) != null; ) {
out.putNextEntry(ze);
// When the bug is present, it shows up here. The second call to
// copyZip will throw an exception while reading data.
for (int nr; 0 < (nr = in.read(buffer)); ) {
out.write(buffer, 0, nr);
}
}
in.close();
}
private static void realMain(String[] args) throws Throwable {
// Create zip output in byte array zipbytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry e = new ZipEntry("testdir/foo");
byte[] data = "entry data".getBytes("ASCII");
zos.putNextEntry(e);
zos.write(data);
zos.close();
byte[] zipbytes1 = baos.toByteArray();
int length1 = zipbytes1.length;
System.out.println("zip bytes pre-copy length=" + length1);
// Make a ZipInputStream around zipbytes, and use
// copyZip to get a new byte array.
ZipInputStream zis =
new ZipInputStream(
new ByteArrayInputStream(zipbytes1));
baos.reset();
zos = new ZipOutputStream(baos);
copyZip(zis, zos);
zos.close();
byte[] zipbytes2 = baos.toByteArray();
int length2 = zipbytes2.length;
// When the bug is present, pre- and post-copy lengths are different!
System.out.println("zip bytes post-copy length=" + length2);
equal(length1, length2);
check(Arrays.equals(zipbytes1, zipbytes2));
// Now use copyZip again on the bytes resulting from the previous
// copy. When the bug is present, copyZip will get an exception this
// time.
baos.reset();
zos = new ZipOutputStream(baos);
copyZip(new ZipInputStream(new ByteArrayInputStream(zipbytes2)), zos);
zos.close();
byte[] zipbytes3 = baos.toByteArray();
int length3 = zipbytes3.length;
System.out.println("zip bytes post-copy length=" + length3);
equal(length1, length3);
check(Arrays.equals(zipbytes1, zipbytes3));
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8321396
* @summary Verify that ZipInputStream ignores non-zero, incorrect 'crc',
* 'compressed size' and 'uncompressed size' values when in streaming mode.
* @run junit DataDescriptorIgnoreCrcAndSizeFields
*/
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.*;
public class DataDescriptorIgnoreCrcAndSizeFields {
/**
* Verify that ZipInputStream correctly ignores values from a LOC header's
* 'crc', 'compressed size' and 'uncompressed size' fields, when in
* streaming mode and these fields are incorrectly set to non-zero values.
*/
@Test
public void shouldIgnoreCrcAndSizeValuesInStreamingMode() throws IOException {
// ZIP with incorrect 'CRC', 'compressed size' and 'uncompressed size' values
byte[] zip = zipWithIncorrectCrcAndSizeValuesInLocalHeader();
// ZipInputStream should ignore the incorrect field values
try (ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(zip))) {
ZipEntry first = in.getNextEntry();
assertNotNull(first, "Zip file is unexpectedly missing first entry");
// CRC, compressed size and size should be uninitialized at this point
assertCrcAndSize(first, -1, -1, -1);
// Check that name and contents is as expected
assertNameAndContents("first", first, in);
// At this point, ZipInputStream should have read correct values from the data descriptor
assertCrcAndSize(first, crc32("first"), compressedSize("first"), uncompressedSize("first"));
// For extra caution, also read and validate the second entry
ZipEntry second = in.getNextEntry();
assertNotNull(second, "Zip file is unexpectedly missing second entry");
// CRC, compressed size and size should be uninitialized at this point
assertCrcAndSize(second, -1, -1, -1);
// Check that name and contents is as expected
assertNameAndContents("second", second, in);
// At this point, ZipInputStream should have read correct values from the data descriptor
assertCrcAndSize(second, crc32("second"), compressedSize("second"), uncompressedSize("second"));
}
}
/**
* Assert that the given ZipEntry has the expected name and that
* the expected content can be read from the ZipInputStream
* @param expected the expected name and content
* @param entry the entry to check the name of
* @param in the ZipInputStream to check the entry content of
* @throws IOException if an IO exception occurs
*/
private static void assertNameAndContents(String expected, ZipEntry entry, ZipInputStream in) throws IOException {
assertEquals(expected, entry.getName());
assertArrayEquals(expected.getBytes(StandardCharsets.UTF_8), in.readAllBytes());
}
/**
* Assert that a ZipEntry has the expected CRC-32, compressed size and uncompressed size values
* @param entry the ZipEntry to validate
* @param expectedCrc the expected CRC-32 value
* @param expectedCompressedSize the exprected compressed size value
* @param expectedSize the expected size value
*/
private static void assertCrcAndSize(ZipEntry entry, long expectedCrc, long expectedCompressedSize, long expectedSize) {
assertEquals(expectedCrc, entry.getCrc());
assertEquals(expectedCompressedSize, entry.getCompressedSize());
assertEquals(expectedSize, entry.getSize());
}
/**
* Return the CRC-32 value for the given string encoded in UTF-8
* @param content the string to produce a CRC-32 checksum for
* @return the CRC-value of the encoded string
*/
private long crc32(String content) {
CRC32 crc32 = new CRC32();
crc32.update(content.getBytes(StandardCharsets.UTF_8));
return crc32.getValue();
}
/**
* Return the length of the given content encoded in UTF-8
* @param content the content to return the encoded length for
* @return the uncompressed size of the encoded content
*/
private long uncompressedSize(String content) {
return content.getBytes(StandardCharsets.UTF_8).length;
}
/**
* Returns the size of the given content, as if it was encoded in UTF-8 and then deflated
* @param content the content to get the compressed size of
* @return the compressed size of the content
* @throws IOException if an IO exception occurs
*/
private long compressedSize(String content) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
try (OutputStream o = new DeflaterOutputStream(bao, new Deflater(Deflater.DEFAULT_COMPRESSION, true))) {
o.write(content.getBytes(StandardCharsets.UTF_8));
}
return bao.size();
}
/**
* When a ZIP entry is created in 'streaming' mode, the 'general purpose bit flag' 3
* is set, and the fields crc-32, compressed size and uncompressed size are set to
* zero in the local header.
*
* Certain legacy ZIP tools incorrectly set non-zero values for one or more of these
* three fields when in streaming mode.
*
* This method creates a ZIP where the first entry has a local header where the
* mentioned fields are set to a non-zero, incorrect values. The second entry
* has the correct zero values for these fields.
*/
private static byte[] zipWithIncorrectCrcAndSizeValuesInLocalHeader() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ZipOutputStream zo = new ZipOutputStream(out)) {
// Write a first entry
zo.putNextEntry(new ZipEntry("first"));
zo.write("first".getBytes(StandardCharsets.UTF_8));
// Add a second entry
zo.putNextEntry(new ZipEntry("second"));
zo.write("second".getBytes(StandardCharsets.UTF_8));
}
// ZipOutputStream correctly produces local headers with zero crc and sizes values
byte[] zip = out.toByteArray();
// Buffer for updating the local header values
ByteBuffer buffer = ByteBuffer.wrap(zip).order(ByteOrder.LITTLE_ENDIAN);
// Set the CRC-32 field to an incorrect value
buffer.putShort(ZipEntry.LOCCRC, (short) 42);
// Set the compressed size to an incorrect value
buffer.putShort(ZipEntry.LOCSIZ, (short) 42);
// Set the uncompressed size to an incorrect value
buffer.putShort(ZipEntry.LOCLEN, (short) 42);
return zip;
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright 2012 Google, Inc. All Rights Reserved.
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8056934
* @summary Verify the ability to read zip files whose local header
* data descriptor is missing the optional signature
* <p>
* No way to adapt the technique in this test to get a ZIP64 zip file
* without data descriptors was found.
* @run junit DataDescriptorSignatureMissing
*/
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.*;
public class DataDescriptorSignatureMissing {
/**
* Verify that ZipInputStream correctly parses a ZIP with a Data Descriptor without
* the recommended but optional signature.
*/
@Test
public void shouldParseSignaturelessDescriptor() throws IOException {
// The ZIP with a signature-less descriptor
byte[] zip = makeZipWithSignaturelessDescriptor();
// ZipInputStream should read the signature-less data descriptor
try (ZipInputStream in = new ZipInputStream(
new ByteArrayInputStream(zip))) {
ZipEntry first = in.getNextEntry();
assertNotNull(first, "Zip file is unexpectedly missing first entry");
assertEquals("first", first.getName());
assertArrayEquals("first".getBytes(StandardCharsets.UTF_8), in.readAllBytes());
ZipEntry second = in.getNextEntry();
assertNotNull(second, "Zip file is unexpectedly missing second entry");
assertEquals("second", second.getName());
assertArrayEquals("second".getBytes(StandardCharsets.UTF_8), in.readAllBytes());
}
}
/**
* The 'Data descriptor' record is used to facilitate ZIP streaming. If the size of an
* entry is unknown at the time the LOC header is written, bit 3 of the General Purpose Bit Flag
* is set, and the File data is immediately followed by the 'Data descriptor' record. This record
* then contains the compressed and uncompressed sizes of the entry and also the CRC value.
*
* The 'Data descriptor' record is usually preceded by the recommended, but optional
* signature value 0x08074b50.
*
* A ZIP entry in streaming mode has the following structure:
*
* ------ Local File Header ------
* 000000 signature 0x04034b50
* 000004 version 20
* 000006 flags 0x0808 # Notice bit 3 is set
* [..] Omitted for brevity
*
* ------ File Data ------
* 000035 data 7 bytes
*
* ------ Data Descriptor ------
* 000042 signature 0x08074b50
* 000046 crc 0x3610a686
* 000050 csize 7
* 000054 size 5
*
* A signature-less data descriptor will look like the following:
*
* ------ Data Descriptor ------
* 000042 crc 0x3610a686
* 000046 csize 7
* 000050 size 5
*
* This method produces a ZIP with two entries, where the first entry
* is made signature-less.
*/
private static byte[] makeZipWithSignaturelessDescriptor() throws IOException {
// Offset of the signed data descriptor
int sigOffset;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ZipOutputStream zo = new ZipOutputStream(out)) {
// Write a first entry
zo.putNextEntry(new ZipEntry("first"));
zo.write("first".getBytes(StandardCharsets.UTF_8));
// Force the data descriptor to be written out
zo.closeEntry();
// Signed data descriptor starts 16 bytes before current offset
sigOffset = out.size() - 4 * Integer.BYTES;
// Add a second entry
zo.putNextEntry(new ZipEntry("second"));
zo.write("second".getBytes(StandardCharsets.UTF_8));
}
// The generated ZIP file with a signed data descriptor
byte[] sigZip = out.toByteArray();
// The offset of the CRC immediately following the 4-byte signature
int crcOffset = sigOffset + Integer.BYTES;
// Create a ZIP file with a signature-less data descriptor for the first entry
ByteArrayOutputStream sigLess = new ByteArrayOutputStream();
sigLess.write(sigZip, 0, sigOffset);
// Skip the signature
sigLess.write(sigZip, crcOffset, sigZip.length - crcOffset);
byte[] siglessZip = sigLess.toByteArray();
// Adjust the CEN offset in the END header
ByteBuffer buffer = ByteBuffer.wrap(siglessZip).order(ByteOrder.LITTLE_ENDIAN);
// Reduce cenOffset by 4 bytes
int cenOff = siglessZip.length - ZipFile.ENDHDR + ZipFile.ENDOFF;
int realCenOff = buffer.getInt(cenOff) - Integer.BYTES;
buffer.putInt(cenOff, realCenOff);
// Adjust the LOC offset in the second CEN header
int cen = realCenOff;
// Skip past the first CEN header
int nlen = buffer.getShort(cen + ZipFile.CENNAM);
cen += ZipFile.CENHDR + nlen;
// Reduce LOC offset by 4 bytes
int locOff = cen + ZipFile.CENOFF;
buffer.putInt(locOff, buffer.getInt(locOff) - Integer.BYTES);
return siglessZip;
}
}

View file

@ -0,0 +1,345 @@
/*
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 7110149 8184306 6341887 8357145
* @summary Test basic deflater and inflater functionality
* @key randomness
* @run main/timeout=480 DeInflate
*/
import java.io.*;
import java.lang.foreign.Arena;
import java.nio.*;
import java.util.*;
import java.util.zip.*;
public class DeInflate {
private static Random rnd = new Random();
static void checkStream(Deflater def, byte[] in, int len,
byte[] out1, byte[] out2, boolean nowrap)
throws Throwable
{
Arrays.fill(out1, (byte)0);
Arrays.fill(out2, (byte)0);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try (DeflaterOutputStream defos = new DeflaterOutputStream(baos, def)) {
defos.write(in, 0, len);
}
out1 = baos.toByteArray();
}
int m = out1.length;
Inflater inf = new Inflater(nowrap);
inf.setInput(out1, 0, m);
int n = inf.inflate(out2);
if (n != len ||
!Arrays.equals(Arrays.copyOf(in, len), Arrays.copyOf(out2, len)) ||
inf.inflate(out2) != 0) {
System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
m, n, len, Arrays.equals(in, out2));
throw new RuntimeException("De/inflater failed:" + def);
}
}
static void checkByteBuffer(Deflater def, Inflater inf,
ByteBuffer in, ByteBuffer out1, ByteBuffer out2,
byte[] expected, int len, byte[] result,
boolean out1ReadOnlyWhenInflate)
throws Throwable {
def.reset();
inf.reset();
def.setInput(in);
def.finish();
int m = def.deflate(out1);
out1.flip();
if (out1ReadOnlyWhenInflate)
out1 = out1.asReadOnlyBuffer();
inf.setInput(out1);
int n = inf.inflate(out2);
out2.flip();
out2.get(result, 0, n);
if (n != len || out2.position() != len ||
!Arrays.equals(Arrays.copyOf(expected, len), Arrays.copyOf(result, len)) ||
inf.inflate(result) != 0) {
throw new RuntimeException("De/inflater(buffer) failed:" + def);
}
}
static void checkByteBufferReadonly(Deflater def, Inflater inf,
ByteBuffer in, ByteBuffer out1, ByteBuffer out2)
throws Throwable {
def.reset();
inf.reset();
def.setInput(in);
def.finish();
int m = -1;
if (!out2.isReadOnly())
out2 = out2.asReadOnlyBuffer();
try {
m = def.deflate(out2);
throw new RuntimeException("deflater: ReadOnlyBufferException: failed");
} catch (ReadOnlyBufferException robe) {}
m = def.deflate(out1);
out1.flip();
inf.setInput(out1);
try {
inf.inflate(out2);
throw new RuntimeException("inflater: ReadOnlyBufferException: failed");
} catch (ReadOnlyBufferException robe) {}
}
/**
* Uses the {@code def} deflater to deflate the input data {@code in} of length {@code len}.
* A new {@link Inflater} is then created within this method to inflate the deflated data. The
* inflated data is then compared with the {@code in} to assert that it matches the original
* input data.
* This method repeats these checks for the different overloaded methods of
* {@code Deflater.deflate(...)} and {@code Inflater.inflate(...)}
*
* @param def the deflater to use for deflating the contents in {@code in}
* @param in the input content
* @param len the length of the input content to use
* @param nowrap will be passed to the constructor of the {@code Inflater} used in this
* method
* @throws Throwable if any error occurs during the check
*/
static void check(Deflater def, byte[] in, int len, boolean nowrap)
throws Throwable
{
byte[] tempBuffer = new byte[1024];
byte[] out1, out2;
int m = 0, n = 0;
Inflater inf = new Inflater(nowrap);
def.setInput(in, 0, len);
def.finish();
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
while (!def.finished()) {
int temp_counter = def.deflate(tempBuffer);
m += temp_counter;
baos.write(tempBuffer, 0, temp_counter);
}
out1 = baos.toByteArray();
baos.reset();
inf.setInput(out1, 0, m);
while (!inf.finished()) {
int temp_counter = inf.inflate(tempBuffer);
n += temp_counter;
baos.write(tempBuffer, 0, temp_counter);
}
out2 = baos.toByteArray();
if (n != len ||
!Arrays.equals(in, 0, len, out2, 0, len) ||
inf.inflate(out2) != 0) {
System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
m, n, len, Arrays.equals(in, out2));
throw new RuntimeException("De/inflater failed:" + def);
}
}
// readable
Arrays.fill(out1, (byte)0);
Arrays.fill(out2, (byte)0);
ByteBuffer bbIn = ByteBuffer.wrap(in, 0, len);
ByteBuffer bbOut1 = ByteBuffer.wrap(out1);
ByteBuffer bbOut2 = ByteBuffer.wrap(out2);
checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
// readonly in
Arrays.fill(out1, (byte)0);
Arrays.fill(out2, (byte)0);
bbIn = ByteBuffer.wrap(in, 0, len).asReadOnlyBuffer();
bbOut1 = ByteBuffer.wrap(out1);
bbOut2 = ByteBuffer.wrap(out2);
checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
// readonly out1 when inflate
Arrays.fill(out1, (byte)0);
Arrays.fill(out2, (byte)0);
bbIn = ByteBuffer.wrap(in, 0, len);
bbOut1 = ByteBuffer.wrap(out1);
bbOut2 = ByteBuffer.wrap(out2);
checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, true);
checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
// direct
bbIn = ByteBuffer.allocateDirect(in.length);
bbIn.put(in, 0, n).flip();
bbOut1 = ByteBuffer.allocateDirect(out1.length);
bbOut2 = ByteBuffer.allocateDirect(out2.length);
checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
// segment
try (var arena = Arena.ofConfined()) {
bbIn = arena.allocate(in.length).asByteBuffer();
bbIn.put(in, 0, n).flip();
bbOut1 = arena.allocate(out1.length).asByteBuffer();
bbOut2 = arena.allocate(out2.length).asByteBuffer();
checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
}
}
static void checkDict(Deflater def, Inflater inf, byte[] src,
byte[] dstDef, byte[] dstInf,
ByteBuffer dictDef, ByteBuffer dictInf) throws Throwable {
def.reset();
inf.reset();
def.setDictionary(dictDef);
def.setInput(src);
def.finish();
int n = def.deflate(dstDef);
inf.setInput(dstDef, 0, n);
n = inf.inflate(dstInf);
if (n != 0 || !inf.needsDictionary()) {
throw new RuntimeException("checkDict failed: need dict to continue");
}
inf.setDictionary(dictInf);
n = inf.inflate(dstInf);
// System.out.println("result: " + new String(dstInf, 0, n));
if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
throw new RuntimeException("checkDict failed: inflate result");
}
}
static void checkDict(int level, int strategy) throws Throwable {
Deflater def = newDeflater(level, strategy, false, new byte[0]);
Inflater inf = new Inflater();
byte[] src = "hello world, hello world, hello sherman".getBytes();
byte[] dict = "hello".getBytes();
byte[] dstDef = new byte[1024];
byte[] dstInf = new byte[1024];
def.setDictionary(dict);
def.setInput(src);
def.finish();
int n = def.deflate(dstDef);
inf.setInput(dstDef, 0, n);
n = inf.inflate(dstInf);
if (n != 0 || !inf.needsDictionary()) {
throw new RuntimeException("checkDict failed: need dict to continue");
}
inf.setDictionary(dict);
n = inf.inflate(dstInf);
//System.out.println("result: " + new String(dstInf, 0, n));
if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
throw new RuntimeException("checkDict failed: inflate result");
}
ByteBuffer dictDef = ByteBuffer.wrap(dict);
ByteBuffer dictInf = ByteBuffer.wrap(dict);
checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
dictDef = ByteBuffer.allocateDirect(dict.length);
dictInf = ByteBuffer.allocateDirect(dict.length);
dictDef.put(dict).flip();
dictInf.put(dict).flip();
checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
def.end();
inf.end();
}
private static Deflater newDeflater(int level, int strategy, boolean dowrap, byte[] tmp) {
Deflater def = new Deflater(level, dowrap);
if (strategy != Deflater.DEFAULT_STRATEGY) {
def.setStrategy(strategy);
// The first invocation after setLevel/Strategy()
// with a different level/stragety returns 0, if
// there is no need to flush out anything for the
// previous setting/"data", this is tricky and
// appears un-documented.
def.deflate(tmp);
}
return def;
}
private static Deflater resetDeflater(Deflater def, int level, int strategy) {
def.setLevel(level);
def.setStrategy(strategy);
def.reset();
return def;
}
public static void main(String[] args) throws Throwable {
byte[] dataIn = new byte[1024 * 512];
rnd.nextBytes(dataIn);
byte[] dataOut1 = new byte[dataIn.length + 1024];
byte[] dataOut2 = new byte[dataIn.length];
Deflater defNotWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
Deflater defWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
for (int level = Deflater.DEFAULT_COMPRESSION;
level <= Deflater.BEST_COMPRESSION; level++) {
for (int strategy = Deflater.DEFAULT_STRATEGY;
strategy <= Deflater.HUFFMAN_ONLY; strategy++) {
for (boolean dowrap : new boolean[] { false, true }) {
System.out.println("level:" + level +
", strategy: " + strategy +
", dowrap: " + dowrap);
for (int i = 0; i < 5; i++) {
int len = (i == 0)? dataIn.length
: new Random().nextInt(dataIn.length);
System.out.println("iteration: " + (i + 1) + " input length: " + len);
// use a new deflater
Deflater def = newDeflater(level, strategy, dowrap, dataOut2);
check(def, dataIn, len, dowrap);
def.end();
// reuse the deflater (with reset) and test on stream, which
// uses a "smaller" buffer (smaller than the overall data)
def = resetDeflater(dowrap ? defWrap : defNotWrap, level, strategy);
checkStream(def, dataIn, len, dataOut1, dataOut2, dowrap);
}
}
// test setDictionary()
checkDict(level, strategy);
}
}
}
}

View file

@ -0,0 +1,392 @@
/*
* Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4679743
* @summary Test basic functionality of DeflaterInputStream and InflaterOutputStream
* @key randomness
*/
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.*;
public class DeflateIn_InflateOut {
private static byte[] data = new byte[1024 * 1024];
private static ByteArrayInputStream bais;
private static DeflaterInputStream dis;
private static ByteArrayOutputStream baos;
private static InflaterOutputStream ios;
private static Inflater reset(byte[] dict) {
bais = new ByteArrayInputStream(data);
if (dict == null) {
dis = new DeflaterInputStream(bais);
} else {
Deflater def = new Deflater();
def.setDictionary(dict);
dis = new DeflaterInputStream(bais, def);
}
baos = new ByteArrayOutputStream();
if (dict == null) {
ios = new InflaterOutputStream(baos);
return null;
} else {
Inflater inf = new Inflater();
ios = new InflaterOutputStream(baos, inf);
return inf;
}
}
private static void reset() {
reset(null);
}
/** Check byte arrays read/write. */
private static void ArrayReadWrite() throws Throwable {
byte[] buf = new byte[512];
reset();
check(dis.available() == 1);
for (;;) {
int len = dis.read(buf, 0, buf.length);
if (len < 0) {
break;
} else {
ios.write(buf, 0, len);
}
}
check(dis.available() == 0);
ios.close();
check(Arrays.equals(data, baos.toByteArray()));
}
/** Check byte arrays read, single byte write */
private static void ArrayReadByteWrite() throws Throwable {
byte[] buf = new byte[512];
reset();
for (;;) {
int len = dis.read(buf, 0, buf.length);
if (len <= 0) {
break;
} else {
for (int i = 0; i < len; i++) {
byte x = (byte) (buf[i] & 0xff);
ios.write(x);
}
}
}
check(dis.available() == 0);
ios.close();
check(Arrays.equals(data, baos.toByteArray()));
}
/** Check single byte read, byte array write.
* <p>
* Note that this test relies on the vaule from DeflaterInputStream.read()
* to determine when to stop reading.
*/
private static void ByteReadArrayWrite() throws Throwable {
byte[] buf = new byte[8192];
int off = 0;
reset();
int datum = dis.read();
while (datum != -1) {
if (off == 8192) {
ios.write(buf, 0, off);
off = 0;
}
buf[off++] = (byte) (datum & 0xff);
datum = dis.read();
}
if (off > 0) {
ios.write(buf, 0, off);
}
ios.close();
check(Arrays.equals(data, baos.toByteArray()));
}
/** Check single byte read/write.
* <p>
* Note that this test relies on DeflaterInputStream.available() to
* determine when to stop reading.
*/
private static void ByteReadByteWrite() throws Throwable {
byte[] buf = new byte[512];
boolean reachEOF = false;
reset();
while (dis.available() == 1) {
int datum = dis.read();
if (datum == -1) {
reachEOF = true;
} else {
if (datum < 0 || datum > 255) {
fail("datum out of range: " + datum);
}
ios.write(datum);
}
}
dis.close();
ios.close();
check(data[0] == baos.toByteArray()[0]);
}
/** Check skip(). */
private static void SkipBytes() throws Throwable {
byte[] buf = new byte[512];
int numReadable = 0;
// Count number of bytes that are read
reset();
check(dis.available() == 1);
for (;;) {
int count = dis.read(buf, 0, buf.length);
if (count < 0) {
break;
} else {
numReadable += count;
}
}
check(dis.available() == 0);
// Verify that skipping the first several bytes works.
reset();
int numNotSkipped = 0;
int numSkipBytes = 2053; // arbitrarily chosen prime
check(dis.skip(numSkipBytes) == numSkipBytes);
for (int i = 0; ; i++) {
int count = dis.read(buf, 0, buf.length);
if (count < 0) {
break;
} else {
numNotSkipped += count;
}
}
check(numNotSkipped + numSkipBytes == numReadable);
// Verify that skipping some bytes mid-stream works.
reset();
numNotSkipped = 0;
numSkipBytes = 8887; // arbitrarily chosen prime
for (int i = 0; ; i++) {
if (i == 13) { // Arbitrarily chosen
check(dis.skip(numSkipBytes) == numSkipBytes);
} else {
int count = dis.read(buf, 0, buf.length);
if (count < 0) {
break;
} else {
numNotSkipped += count;
}
}
}
check(numNotSkipped + numSkipBytes == numReadable);
// Verify that skipping the last N bytes works.
reset();
numNotSkipped = 0;
numSkipBytes = 6449; // arbitrarily chosen prime
for (int i = 0; ; i++) {
if (numNotSkipped + numSkipBytes > numReadable) {
numSkipBytes = numReadable - numNotSkipped;
check(dis.skip(numSkipBytes) == numSkipBytes);
check(dis.read(buf, 0, buf.length) == -1);
check(dis.available() == 0);
} else {
int count = dis.read(buf, 0, buf.length);
if (count < 0) {
break;
} else {
numNotSkipped += count;
}
}
}
check(numNotSkipped + numSkipBytes == numReadable);
}
/** Check "needsDictionary()". */
private static void NeedsDictionary() throws Throwable {
byte[] dict = {1, 2, 3, 4};
Adler32 adler32 = new Adler32();
adler32.update(dict);
long checksum = adler32.getValue();
byte[] buf = new byte[512];
Inflater inf = reset(dict);
check(dis.available() == 1);
boolean dictSet = false;
for (;;) {
int len = dis.read(buf, 0, buf.length);
if (len < 0) {
break;
} else {
try {
ios.write(buf, 0, len);
if (dictSet == false) {
check(false, "Must throw ZipException without dictionary");
return;
}
} catch (ZipException ze) {
check(dictSet == false, "Dictonary must be set only once");
check(checksum == inf.getAdler(), "Incorrect dictionary");
inf.setDictionary(dict);
// After setting the dictionary, we have to flush the
// InflaterOutputStream now in order to consume all the
// pending input data from the last, failed call to "write()".
ios.flush();
dictSet = true;
}
}
}
check(dis.available() == 0);
ios.close();
check(Arrays.equals(data, baos.toByteArray()));
}
/**
* Verifies that when a DeflaterInputStream is constructed
* by passing a Deflater instance, then closing the DeflaterInputStream
* will not close the passed Deflater instance.
*/
private static void deflaterInputStreamDeflaterNotClosed() throws Throwable {
// some arbitrary content
final byte[] original = "foo".repeat(1024).getBytes(StandardCharsets.US_ASCII);
// run the DeflaterInputStream tests
try (final Deflater def = new Deflater()) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(original);
DeflaterInputStream iis = new DeflaterInputStream(bis, def)) {
iis.readAllBytes();
}
// verify the deflater wasn't closed - reset() will throw IllegalStateException if
// the deflater is closed
def.reset();
// repeat the test with the other constructor
try (ByteArrayInputStream bis = new ByteArrayInputStream(original);
DeflaterInputStream iis = new DeflaterInputStream(bis, def, 1024)) {
iis.readAllBytes();
}
// verify the deflater wasn't closed - reset() will throw IllegalStateException if
// the deflater is closed
def.reset();
}
}
private static byte[] deflate(final byte[] original) {
final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream();
try (Deflater compressor = new Deflater()) {
compressor.setInput(original);
compressor.finish();
while (!compressor.finished()) {
byte[] tmpBuffer = new byte[1024];
int numCompressed = compressor.deflate(tmpBuffer);
compressedBaos.write(tmpBuffer, 0, numCompressed);
}
}
return compressedBaos.toByteArray();
}
/**
* Verifies that when a InflaterOutputStream is constructed
* by passing a Inflater instance, then closing the InflaterOutputStream
* will not close the passed Inflater instance.
*/
private static void inflaterOutputStreamInflaterNotClosed() throws Throwable {
// some arbitrary content
final byte[] original = "bar".repeat(1024).getBytes(StandardCharsets.US_ASCII);
// deflate it
final byte[] deflated = deflate(original);
try (final Inflater infl = new Inflater()) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
InflaterOutputStream dos = new InflaterOutputStream(bos, infl)) {
dos.write(deflated);
dos.flush();
check(Arrays.equals(original, bos.toByteArray()));
}
// verify the inflater wasn't closed - reset() will throw IllegalStateException if
// the inflater is closed
infl.reset();
// repeat the test with the other constructor
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
InflaterOutputStream dos = new InflaterOutputStream(bos, infl, 1024)) {
dos.write(deflated);
dos.flush();
check(Arrays.equals(original, bos.toByteArray()));
}
// verify the inflater wasn't closed - reset() will throw IllegalStateException if
// the inflater is closed
infl.reset();
}
}
public static void realMain(String[] args) throws Throwable {
new Random(new Date().getTime()).nextBytes(data);
ArrayReadWrite();
ArrayReadByteWrite();
ByteReadArrayWrite();
ByteReadByteWrite();
SkipBytes();
NeedsDictionary();
deflaterInputStreamDeflaterNotClosed();
inflaterOutputStreamInflaterNotClosed();
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() { fail(null); }
static void fail(String msg) {
failed++;
if (msg != null) {
System.err.println(msg);
}
Thread.dumpStack();
}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void check(boolean cond, String msg) {if (cond) pass(); else fail(msg);}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,223 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.Deflater;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*
* @test
* @bug 8225763
* @summary Test that the close() and end() methods on java.util.zip.Deflater
* @run junit DeflaterClose
*/
public class DeflaterClose {
private static final String data = "foobarhelloworld!!!!";
/**
* Closes the Deflater multiple times and then expects close() and end() each
* to be called that many times.
*/
@Test
public void testCloseMultipleTimes() throws Exception {
final int numTimes = 3;
final Deflater simpleDeflater = new Deflater();
closeMultipleTimesAfterCompressing(numTimes, simpleDeflater);
final OverrideClose overriddenClose = new OverrideClose();
closeMultipleTimesAfterCompressing(numTimes, overriddenClose);
// make sure close was called numTimes
assertEquals(numTimes, overriddenClose.numTimesCloseCalled, "close() was expected to be" +
" called " + numTimes + ", but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenClose.getClass().getName());
final OverrideEnd overriddenEnd = new OverrideEnd();
closeMultipleTimesAfterCompressing(numTimes, overriddenEnd);
// make sure end was called called numTimes
assertEquals(numTimes, overriddenEnd.numTimesEndCalled, "end() was expected to be called " +
numTimes + ", but was called " + overriddenEnd.numTimesEndCalled + " time(s) on " +
overriddenEnd.getClass().getName());
final OverrideCloseAndEnd overriddenCloseAndEnd = new OverrideCloseAndEnd();
closeMultipleTimesAfterCompressing(numTimes, overriddenCloseAndEnd);
// make sure end was called called numTimes
assertEquals(numTimes, overriddenCloseAndEnd.numTimesEndCalled, "end() was expected to be called " +
numTimes + ", but was called" + overriddenCloseAndEnd.numTimesEndCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
assertEquals(numTimes, overriddenCloseAndEnd.numTimesCloseCalled, "close() was expected" +
" to be called " + numTimes + ", but was called " +
overriddenClose.numTimesCloseCalled + " time(s) on " +
overriddenCloseAndEnd.getClass().getName());
}
/**
* Closes the Deflater first and then calls end(). Verifies that close() was called
* just once but end() was called twice (once internally through close() and once
* explicitly)
*/
@Test
public void testCloseThenEnd() throws Exception {
final Deflater simpleDeflater = new Deflater();
compressCloseThenEnd(simpleDeflater);
final OverrideClose overriddenClose = new OverrideClose();
compressCloseThenEnd(overriddenClose);
// make sure close was called once
assertEquals(1, overriddenClose.numTimesCloseCalled, "close() was expected to be called" +
" once, but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenClose.getClass().getName());
final OverrideEnd overriddenEnd = new OverrideEnd();
compressCloseThenEnd(overriddenEnd);
// make sure end was called twice (once through close() and then explicitly)
assertEquals(2, overriddenEnd.numTimesEndCalled, "end() was expected to be called" +
" twice, but was called " + overriddenEnd.numTimesEndCalled +
" time(s) on " + overriddenEnd.getClass().getName());
final OverrideCloseAndEnd overriddenCloseAndEnd = new OverrideCloseAndEnd();
compressCloseThenEnd(overriddenCloseAndEnd);
// make sure end was called twice (once through close and once explicitly)
// and close was called once
assertEquals(2, overriddenCloseAndEnd.numTimesEndCalled, "end() was expected to" +
" be called twice, but was called " + overriddenCloseAndEnd.numTimesEndCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
assertEquals(1, overriddenCloseAndEnd.numTimesCloseCalled, "close() was expected to be" +
" called once, but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
}
/**
* Calls end() on the Deflater first and then calls close(). Verifies that close() was called
* just once and end() twice.
*/
@Test
public void testEndThenClose() throws Exception {
final Deflater simpleDeflater = new Deflater();
compressEndThenClose(simpleDeflater);
final OverrideClose overriddenClose = new OverrideClose();
compressEndThenClose(overriddenClose);
// make sure close was called once
assertEquals(1, overriddenClose.numTimesCloseCalled, "close() was expected to be called" +
" once, but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenClose.getClass().getName());
final OverrideEnd overriddenEnd = new OverrideEnd();
compressEndThenClose(overriddenEnd);
// make sure end was called twice (once through the explicit end call and
// once through close())
assertEquals(2, overriddenEnd.numTimesEndCalled, "end() was expected to be called twice," +
" but was called " + overriddenEnd.numTimesEndCalled +
" time(s) on " + overriddenEnd.getClass().getName());
final OverrideCloseAndEnd overriddenCloseAndEnd = new OverrideCloseAndEnd();
compressEndThenClose(overriddenCloseAndEnd);
// make sure end was called twice (once through the explicit end call and
// once through close())
assertEquals(2, overriddenCloseAndEnd.numTimesEndCalled, "end() was expected to be called" +
" twice, but was called " + overriddenCloseAndEnd.numTimesEndCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
assertEquals(1, overriddenCloseAndEnd.numTimesCloseCalled, "close() was expected to be " +
"called once, but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
}
private void closeMultipleTimesAfterCompressing(final int numTimes, final Deflater deflater) {
compress(deflater);
// call close() multiple times
for (int i = 0; i < numTimes; i++) {
deflater.close();
}
}
private void compressCloseThenEnd(final Deflater deflater) {
// compress the data then close() and then end()
try (final Deflater compressor = deflater) {
compress(compressor);
}
deflater.end();
}
private void compressEndThenClose(final Deflater deflater) {
// compress the data then end() and then close()
try (final Deflater compressor = deflater) {
compress(compressor);
// end() it first before it's (auto)closed by the try-with-resources
compressor.end();
}
}
private static byte[] compress(final Deflater deflater) {
deflater.setInput(data.getBytes(StandardCharsets.UTF_8));
deflater.finish();
final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream();
while (!deflater.finished()) {
final byte[] tmpBuffer = new byte[100];
final int numCompressed = deflater.deflate(tmpBuffer);
compressedBaos.write(tmpBuffer, 0, numCompressed);
}
return compressedBaos.toByteArray();
}
private static final class OverrideEnd extends Deflater {
private int numTimesEndCalled = 0;
@Override
public void end() {
this.numTimesEndCalled++;
super.end();
}
}
private static final class OverrideClose extends Deflater {
private int numTimesCloseCalled = 0;
@Override
public void close() {
this.numTimesCloseCalled++;
super.close();
}
}
private static final class OverrideCloseAndEnd extends Deflater {
private int numTimesEndCalled = 0;
private int numTimesCloseCalled = 0;
@Override
public void end() {
this.numTimesEndCalled++;
super.end();
}
@Override
public void close() {
this.numTimesCloseCalled++;
super.close();
}
}
}

View file

@ -0,0 +1,247 @@
/*
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @test
* @bug 8252739
* @summary Verify Deflater.setDictionary(dictionary, offset, length) uses the offset
* @run junit/othervm DeflaterDictionaryTests
*/
public class DeflaterDictionaryTests {
// Output buffer size
private static final int RESULT_SIZE = 1024;
// Data to compress
private static final String SRC_DATA = "Welcome to the US Open;".repeat(6);
// Dictionary to be used
private static final String DICTIONARY = "US Open";
private static final int DICTIONARY_OFFSET = 1;
private static final int DICTIONARY_LENGTH = 3;
/**
* MethodSource with offsets which should be valid for setDictionary
*
* @return Stream of valid offset values
*/
protected static IntStream validDictionaryOffsets() {
return IntStream.of(0, DICTIONARY_OFFSET, DICTIONARY_LENGTH);
}
/**
* MethodSource with invalid offsets for setDictionary
*
* @return Stream of invalid offset values
*/
protected static IntStream invalidDictionaryOffsets() {
return IntStream.of(-1, DICTIONARY_LENGTH + 2, DICTIONARY.length());
}
/**
* Validate that an offset can be used with Deflater::setDictionary
*
* @param dictionary_offset offset value to be used
* @throws Exception if an error occurs
*/
@ParameterizedTest
@MethodSource("validDictionaryOffsets")
public void testByteArray(int dictionary_offset) throws Exception {
byte[] input = SRC_DATA.getBytes(UTF_8);
byte[] output = new byte[RESULT_SIZE];
Deflater deflater = new Deflater();
Inflater inflater = new Inflater();
try {
// Compress the bytes
deflater.setDictionary(DICTIONARY.getBytes(UTF_8), dictionary_offset, DICTIONARY_LENGTH);
deflater.setInput(input);
deflater.finish();
int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH);
System.out.printf("Deflater::getBytesWritten:%d, Deflater::getAdler: %d," +
" compressed length: %d%n", deflater.getBytesWritten(),
deflater.getAdler(), compressedDataLength);
deflater.finished();
// Decompress the bytes
inflater.setInput(output, 0, compressedDataLength);
byte[] result = new byte[RESULT_SIZE];
int resultLength = inflater.inflate(result);
if (inflater.needsDictionary()) {
System.out.println("Specifying Dictionary");
inflater.setDictionary(DICTIONARY.getBytes(UTF_8), dictionary_offset, DICTIONARY_LENGTH);
resultLength = inflater.inflate(result);
} else {
System.out.println("Did not need to use a Dictionary");
}
inflater.finished();
System.out.printf("Inflater::getAdler:%d, length: %d%n",
inflater.getAdler(), resultLength);
assertEquals(SRC_DATA.length(), resultLength);
assertArrayEquals(input, Arrays.copyOf(result, resultLength));
} finally {
// Release Resources
deflater.end();
inflater.end();
}
}
/**
* Validate that a ByteBuffer can be used with Deflater::setDictionary
*
* @throws Exception if an error occurs
*/
@Test
public void testHeapByteBuffer() throws Exception {
byte[] input = SRC_DATA.getBytes(UTF_8);
byte[] output = new byte[RESULT_SIZE];
ByteBuffer dictDef = ByteBuffer.wrap(DICTIONARY.getBytes(UTF_8), DICTIONARY_OFFSET, DICTIONARY_LENGTH);
ByteBuffer dictInf = ByteBuffer.wrap(DICTIONARY.getBytes(UTF_8), DICTIONARY_OFFSET, DICTIONARY_LENGTH);
Deflater deflater = new Deflater();
Inflater inflater = new Inflater();
try {
// Compress the bytes
deflater.setDictionary(dictDef);
deflater.setInput(input);
deflater.finish();
int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH);
System.out.printf("Deflater::getBytesWritten:%d, Deflater::getAdler: %d," +
" compressed length: %d%n", deflater.getBytesWritten(),
deflater.getAdler(), compressedDataLength);
deflater.finished();
// Decompress the bytes
inflater.setInput(output, 0, compressedDataLength);
byte[] result = new byte[RESULT_SIZE];
int resultLength = inflater.inflate(result);
if (inflater.needsDictionary()) {
System.out.println("Specifying Dictionary");
inflater.setDictionary(dictInf);
resultLength = inflater.inflate(result);
} else {
System.out.println("Did not need to use a Dictionary");
}
inflater.finished();
System.out.printf("Inflater::getAdler:%d, length: %d%n",
inflater.getAdler(), resultLength);
assertEquals(resultLength, SRC_DATA.length());
assertArrayEquals(Arrays.copyOf(result, resultLength), input);
} finally {
// Release Resources
deflater.end();
inflater.end();
}
}
/**
* Validate that ByteBuffer::allocateDirect can be used with Deflater::setDictionary
*
* @throws Exception if an error occurs
*/
@Test
public void testByteBufferDirect() throws Exception {
byte[] input = SRC_DATA.getBytes(UTF_8);
byte[] output = new byte[RESULT_SIZE];
ByteBuffer dictDef = ByteBuffer.allocateDirect(DICTIONARY.length());
ByteBuffer dictInf = ByteBuffer.allocateDirect(DICTIONARY.length());
dictDef.put(DICTIONARY.getBytes(UTF_8));
dictInf.put(DICTIONARY.getBytes(UTF_8));
dictDef.position(DICTIONARY_OFFSET);
dictDef.limit(DICTIONARY_LENGTH);
dictInf.position(DICTIONARY_OFFSET);
dictInf.limit(DICTIONARY_LENGTH);
Deflater deflater = new Deflater();
Inflater inflater = new Inflater();
try {
// Compress the bytes
deflater.setDictionary(dictDef.slice());
deflater.setInput(input);
deflater.finish();
int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH);
System.out.printf("Deflater::getBytesWritten:%d, Deflater::getAdler: %d," +
" compressed length: %d%n", deflater.getBytesWritten(),
deflater.getAdler(), compressedDataLength);
deflater.finished();
// Decompress the bytes
inflater.setInput(output, 0, compressedDataLength);
byte[] result = new byte[RESULT_SIZE];
int resultLength = inflater.inflate(result);
if (inflater.needsDictionary()) {
System.out.println("Specifying Dictionary");
inflater.setDictionary(dictInf.slice());
resultLength = inflater.inflate(result);
} else {
System.out.println("Did not need to use a Dictionary");
}
inflater.finished();
System.out.printf("Inflater::getAdler:%d, length: %d%n",
inflater.getAdler(), resultLength);
assertEquals(resultLength, SRC_DATA.length());
assertArrayEquals(Arrays.copyOf(result, resultLength), input);
} finally {
// Release Resources
deflater.end();
inflater.end();
}
}
/**
* Validate that an invalid offset used with setDictionary will
* throw an Exception
*
* @param dictionary_offset offset value to be used
*/
@ParameterizedTest
@MethodSource("invalidDictionaryOffsets")
public void testInvalidOffsets(int dictionary_offset) {
byte[] dictionary = DICTIONARY.getBytes(UTF_8);
Deflater deflater = new Deflater();
Inflater inflater = new Inflater();
try {
assertThrows(ArrayIndexOutOfBoundsException.class, () ->
deflater.setDictionary(dictionary, dictionary_offset, DICTIONARY_LENGTH));
assertThrows(ArrayIndexOutOfBoundsException.class, () ->
inflater.setDictionary(dictionary, dictionary_offset, DICTIONARY_LENGTH));
} finally {
// Release Resources
deflater.end();
inflater.end();
}
}
}

View file

@ -0,0 +1,175 @@
/*
* Copyright (c) 2013 Google Inc. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @summary Test java.util.zip behavior with ~64k entries
* @library /test/lib
* @run main/othervm EntryCount64k
* @run main/othervm -Djdk.util.zip.inhibitZip64=true EntryCount64k
* @run main/othervm -Djdk.util.zip.inhibitZip64=false EntryCount64k
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
public class EntryCount64k {
private static final String MAIN_CLASS_MSG = "foo bar hello world Main";
public static class Main {
public static void main(String[] args) {
System.out.println(MAIN_CLASS_MSG);
}
}
static final String MAIN_CLASS = "EntryCount64k$Main";
static final String THIS_CLASS = "EntryCount64k";
static final String[] SPECIAL_CLASSES = { MAIN_CLASS, THIS_CLASS };
// static final String[] SPECIAL_CLASSES = { MAIN_CLASS };
static final int SPECIAL_COUNT = 1 + SPECIAL_CLASSES.length;
public static void main(String[] args) throws Throwable {
for (int i = (1 << 16) - 3; i < (1 << 16) + 2; i++)
test(i);
}
static void test(int entryCount) throws Throwable {
File zipFile = new File("EntryCount64k-tmp.zip");
zipFile.delete();
try (FileOutputStream fos = new FileOutputStream(zipFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos)) {
// Add entries to allow the zip file to be used with "java -jar"
zos.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
for (String line : new String[] {
"Manifest-Version: 1.0",
"Main-Class: " + MAIN_CLASS,
})
zos.write((line + "\n").getBytes("US-ASCII"));
zos.closeEntry();
String testClasses = System.getProperty("test.classes");
for (String className : SPECIAL_CLASSES) {
String baseName = className + ".class";
ZipEntry ze = new ZipEntry(baseName);
File file = new File(testClasses, baseName);
zos.putNextEntry(ze);
Files.copy(file.toPath(), zos);
zos.closeEntry();
}
for (int i = SPECIAL_COUNT; i < entryCount; i++) {
zos.putNextEntry(new ZipEntry(Integer.toString(i)));
zos.closeEntry();
}
}
String p = System.getProperty("jdk.util.zip.inhibitZip64");
boolean tooManyEntries = entryCount >= (1 << 16) - 1;
boolean shouldUseZip64 = tooManyEntries & !("true".equals(p));
boolean usesZip64 = usesZip64(zipFile);
String details = String.format
("entryCount=%d shouldUseZip64=%s usesZip64=%s zipSize=%d%n",
entryCount, shouldUseZip64, usesZip64, zipFile.length());
System.err.println(details);
checkCanRead(zipFile, entryCount);
if (shouldUseZip64 != usesZip64)
throw new Error(details);
zipFile.delete();
}
static boolean usesZip64(File zipFile) throws Exception {
RandomAccessFile raf = new RandomAccessFile(zipFile, "r");
byte[] buf = new byte[4096];
raf.seek(raf.length() - buf.length);
raf.read(buf);
for (int i = 0; i < buf.length - 4; i++) {
// Look for ZIP64 End Header Signature
// Phil Katz: yes, we will always remember you
if (buf[i+0] == 'P' &&
buf[i+1] == 'K' &&
buf[i+2] == 6 &&
buf[i+3] == 6)
return true;
}
return false;
}
static void checkCanRead(File zipFile, int entryCount) throws Throwable {
// Check ZipInputStream API
try (FileInputStream fis = new FileInputStream(zipFile);
BufferedInputStream bis = new BufferedInputStream(fis);
ZipInputStream zis = new ZipInputStream(bis)) {
for (int i = 0; i < entryCount; i++) {
ZipEntry e = zis.getNextEntry();
if (i >= SPECIAL_COUNT) // skip special entries
if (Integer.parseInt(e.getName()) != i)
throw new AssertionError(e.getName());
}
if (zis.getNextEntry() != null)
throw new AssertionError();
}
// Check ZipFile API
try (ZipFile zf = new ZipFile(zipFile)) {
Enumeration<? extends ZipEntry> en = zf.entries();
for (int i = 0; i < entryCount; i++) {
ZipEntry e = en.nextElement();
if (i >= SPECIAL_COUNT) // skip special entries
if (Integer.parseInt(e.getName()) != i)
throw new AssertionError();
}
if (en.hasMoreElements()
|| (zf.size() != entryCount)
|| (zf.getEntry(Integer.toString(entryCount - 1)) == null)
|| (zf.getEntry(Integer.toString(entryCount)) != null))
throw new AssertionError();
}
// Check java -jar
OutputAnalyzer a = ProcessTools.executeTestJava("-jar", zipFile.getName());
a.shouldHaveExitValue(0);
// expect the message from the application on stdout
a.stdoutContains(MAIN_CLASS_MSG);
// nothing is expected on stderr (apart from any probable deprecation
// warnings from the launcher/JVM)
a.stderrShouldMatchIgnoreDeprecatedWarnings("\\A\\Z");
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* Driven by: 3GBZipFiles.sh
*/
import java.io.RandomAccessFile;
import java.io.IOException;
import java.util.Random;
public class FileBuilder {
private static void usageError() {
System.err.println("Usage: FileBuilder filetype filename filesize");
System.err.println("");
System.err.println("Makes a file named FILENAME of size FILESIZE.");
System.err.println("If FILETYPE is \"MostlyEmpty\", the file contents is mostly null bytes");
System.err.println("(which might occupy no disk space if the right OS support exists).");
System.err.println("If FILETYPE is \"SlightlyCompressible\", the file contents are");
System.err.println("approximately 90% random data.");
System.exit(1);
}
public static void main (String[] args) throws IOException {
if (args.length != 3)
usageError();
String filetype = args[0];
String filename = args[1];
long filesize = Long.parseLong(args[2]);
if (! (filetype.equals("MostlyEmpty") ||
filetype.equals("SlightlyCompressible")))
usageError();
try (RandomAccessFile raf = new RandomAccessFile(filename, "rw")) {
if (filetype.equals("SlightlyCompressible")) {
byte[] randomBytes = new byte[16384];
byte[] nullBytes = new byte[randomBytes.length/10];
Random rand = new Random();
for (int i = 0; raf.length() < filesize; ++i) {
rand.nextBytes(randomBytes);
raf.write(nullBytes);
raf.write(randomBytes);
}
}
// Make sure file is exactly the requested size, and that
// a unique identifying trailer is written.
byte[] filenameBytes = filename.getBytes("UTF8");
raf.seek(filesize-filenameBytes.length);
raf.write(filenameBytes);
raf.setLength(filesize);
}
}
}

View file

@ -0,0 +1,295 @@
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* ZIP inflater/deflater performance.
*/
/*
* Run this test on JDK 6 and on later
* JDKs to compare results:
* java -server -Xms1024M -Xmx1024M -Ddebug=true FlaterCriticalArray
*
* The performance issues can be readily seen on JDK 6, and so this code is
* written to compile on that platform: it does *not* use any JDK 7 - specific
* features.
*/
import java.io.*;
import java.nio.*;
import java.util.*;
import java.util.zip.*;
public class FlaterCriticalArray {
// If true, print information about performance
private static final boolean debug = System.getProperty("debug") != null;
private static void debug(String s) {
if (debug) System.out.println(s);
}
private static void debug(String name, String inOut, long time, int length) {
debug(name + ": Duration of " + inOut + "(in ms): " + time);
debug(name + ": " + inOut + "d data length: " + length + " bytes");
}
private static byte[] grow(byte[] a, int capacity) {
while (a.length < capacity) {
byte[] a2 = new byte[a.length * 2];
System.arraycopy(a, 0, a2, 0, a.length);
a = a2;
}
return a;
}
private static byte[] trim(byte[] a, int length) {
byte[] res = new byte[length];
System.arraycopy(a, 0, res, 0, length);
return res;
}
/*
* Base class for individual test cases
*/
private abstract static class TestCase {
protected String name; // For information in debug messages
protected byte data[]; // Data to be deflated and subsequently inflated
protected int level; // Compression level for deflater
protected TestCase(String name, byte data[]) {
this(name, data, -1);
}
protected TestCase(String name, byte data[], int level) {
this.name = name;
this.data = data;
this.level = level;
}
public void runTest() throws Throwable {
long time0, time1;
byte deflated[], inflated[];
debug("");
time0 = System.currentTimeMillis();
deflated = deflate(data, level);
time1 = System.currentTimeMillis();
inform("Deflate", time1 - time0, deflated.length);
time0 = System.currentTimeMillis();
inflated = inflate(deflated);
time1 = System.currentTimeMillis();
inform("Inflate", time1 - time0, inflated.length);
check(Arrays.equals(data, inflated),
name + ": Inflated and deflated arrays do not match");
}
private void inform(String inOut, long duration, int length) {
debug(name, inOut, duration, length);
}
protected abstract byte[] deflate(byte data[], int level) throws Throwable;
protected abstract byte[] inflate(byte deflated[]) throws Throwable;
}
/*
* Following are the individual test cases
*/
private static class StrideTest extends TestCase {
static final int STRIDE = 1024;
public StrideTest(byte data[], int level) {
super("STRIDE", data, level);
}
protected byte[] deflate(byte in[], int level) throws Throwable {
final int len = in.length;
final Deflater deflater = new Deflater(level);
final byte[] smallBuffer = new byte[32];
byte[] flated = new byte[32];
int count = 0;
for (int i = 0; i<len; i+= STRIDE) {
deflater.setInput(in, i, Math.min(STRIDE, len-i));
while (!deflater.needsInput()) {
int n = deflater.deflate(smallBuffer);
flated = grow(flated, count + n);
System.arraycopy(smallBuffer, 0, flated, count, n);
count += n;
}
}
deflater.finish();
int n;
do {
n = deflater.deflate(smallBuffer);
flated = grow(flated, count + n);
System.arraycopy(smallBuffer, 0, flated, count, n);
count += n;
} while (n > 0);
return trim(flated, count);
}
protected byte[] inflate(byte in[]) throws Throwable {
final int len = in.length;
final Inflater inflater = new Inflater();
final byte[] smallBuffer = new byte[3200];
byte[] flated = new byte[32];
int count = 0;
for (int i = 0; i<len; i+= STRIDE) {
inflater.setInput(in, i, Math.min(STRIDE, len-i));
while (!inflater.needsInput()) {
int n;
while ((n = inflater.inflate(smallBuffer)) > 0) {
flated = grow(flated, count + n);
System.arraycopy(smallBuffer, 0, flated, count, n);
count += n;
}
}
}
return trim(flated, count);
}
}
private static class NoStrideTest extends TestCase {
public NoStrideTest(byte data[], int level) {
super("NO STRIDE", data, level);
}
public byte[] deflate(byte in[], int level) throws Throwable {
final Deflater flater = new Deflater(level);
flater.setInput(in);
flater.finish();
final byte[] smallBuffer = new byte[32];
byte[] flated = new byte[32];
int count = 0;
int n;
while ((n = flater.deflate(smallBuffer)) > 0) {
flated = grow(flated, count + n);
System.arraycopy(smallBuffer, 0, flated, count, n);
count += n;
}
return trim(flated, count);
}
public byte[] inflate(byte in[]) throws Throwable {
final Inflater flater = new Inflater();
flater.setInput(in);
final byte[] smallBuffer = new byte[32];
byte[] flated = new byte[32];
int count = 0;
int n;
while ((n = flater.inflate(smallBuffer)) > 0) {
flated = grow(flated, count + n);
System.arraycopy(smallBuffer, 0, flated, count, n);
count += n;
}
return trim(flated, count);
}
}
/**
* Check Deflater{In,Out}putStream by way of GZIP{In,Out}putStream
*/
private static class GZIPTest extends TestCase {
public GZIPTest(byte data[]) {
super("GZIP", data);
}
public byte[] deflate(byte data[], int ignored) throws Throwable {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream gzos = new GZIPOutputStream(baos);
gzos.write(data, 0, data.length);
gzos.close();
return baos.toByteArray();
}
public byte[] inflate(byte deflated[]) throws Throwable {
InputStream bais = new ByteArrayInputStream(deflated);
GZIPInputStream gzis = new GZIPInputStream(bais);
byte[] inflated = new byte[data.length];
int numRead = 0;
int count = 0;
while ((numRead = gzis.read(inflated, count, data.length - count)) > 0) {
count += numRead;
}
check(count == data.length, name + ": Read " + count + "; expected " + data.length);
return inflated;
}
}
public static void realMain(String[] args) throws Throwable {
byte data[];
int level = -1;
if (args.length > 0) {
level = Integer.parseInt(args[0]);
}
debug("Using level " + level);
if (args.length > 1) {
FileInputStream fis = new FileInputStream(args[1]);
int len = fis.available();
data = new byte[len];
check(fis.read(data, 0, len) == len, "Did not read complete file");
debug("Original data from " + args[1]);
fis.close();
} else {
ByteBuffer bb = ByteBuffer.allocate(8);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < 1024 * 64; i++) { // data length
bb.putDouble(0, Math.random());
baos.write(bb.array(), 0, 8);
}
data = baos.toByteArray();
debug("Original data from random byte array");
}
debug("Original data length: " + data.length + " bytes");
new StrideTest(data, level).runTest();
new NoStrideTest(data, level).runTest();
new GZIPTest(data).runTest();
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void pass(String msg) {System.out.println(msg); passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void unexpected(Throwable t, String msg) {
System.out.println(msg); failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static boolean check(boolean cond, String msg) {if (cond) pass(); else fail(msg); return cond;}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,226 @@
/*
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 6348045 6341887 8231770
* @summary GZipOutputStream/InputStream goes critical(calls JNI_Get*Critical)
* and causes slowness. This test uses Deflater and Inflater directly.
* @key randomness
* @run main/othervm -Xcheck:jni FlaterTest
*/
import java.nio.*;
import java.util.*;
import java.util.zip.*;
/**
* This test runs Inflater and Defalter in a number of simultaneous threads,
* validating that the deflated & then inflated data matches the original
* data.
*/
public class FlaterTest extends Thread {
private static final int DATA_LEN = 1024 * 128;
private static ByteBuffer dataDirect;
private static ByteBuffer dataHeap;
// If true, print extra info.
private static final boolean debug = false;
// Set of Flater threads running.
private static Set<Flater> flaters =
Collections.synchronizedSet(new HashSet<>());
/** Fill in {@code data} with random values. */
static void createData() {
ByteBuffer bb = ByteBuffer.allocateDirect(DATA_LEN * 8);
for (int i = 0; i < DATA_LEN * 8; i += 8) {
bb.putDouble(i, Math.random());
}
dataDirect = bb;
final ByteBuffer hb = ByteBuffer.allocate(bb.capacity());
hb.duplicate().put(bb.duplicate());
dataHeap = hb;
if (debug) System.out.println("data length is " + bb.capacity());
}
/** @return the length of the deflated {@code data}. */
private static int getDeflatedLength() {
Deflater deflater = new Deflater();
deflater.setInput(dataDirect.duplicate());
deflater.finish();
byte[] out = new byte[dataDirect.capacity()];
int rc = deflater.deflate(out);
deflater.end();
if (debug) System.out.println("deflatedLength is " + rc);
return rc;
}
/** Compares given bytes with those in {@code data}.
* @throws Exception if given bytes don't match {@code data}.
*/
private static void validate(ByteBuffer buf, int offset, int len) throws Exception {
for (int i = 0; i < len; i++ ) {
if (buf.get(i) != dataDirect.get(offset+i)) {
throw new Exception("mismatch at " + (offset + i));
}
}
}
public static void realMain(String[] args) {
int numThreads = args.length > 0 ? Integer.parseInt(args[0]) : 5;
createData();
for (int srcMode = 0; srcMode <= 2; srcMode ++) {
for (int dstMode = 0; dstMode <= 2; dstMode ++) {
new FlaterTest().go(numThreads, srcMode, dstMode);
}
}
}
private synchronized void go(int numThreads, int srcMode, int dstMode) {
int deflatedLength = getDeflatedLength();
long time = System.currentTimeMillis();
for (int i = 0; i < numThreads; i++) {
Flater f = new Flater(deflatedLength, srcMode, dstMode);
flaters.add(f);
f.start();
}
synchronized (flaters) {
while (flaters.size() != 0) {
try {
flaters.wait();
} catch (InterruptedException ex) {
unexpected(ex);
}
}
}
time = System.currentTimeMillis() - time;
System.out.println("Time needed for " + numThreads
+ " threads to deflate/inflate: " + time + " ms (srcMode="+srcMode+",dstMode="+dstMode+")");
}
/** Deflates and inflates data. */
static class Flater extends Thread {
private final int deflatedLength;
private final int srcMode, dstMode;
private Flater(int length, int srcMode, int dstMode) {
this.deflatedLength = length;
this.srcMode = srcMode;
this.dstMode = dstMode;
}
/** Deflates and inflates {@code data}. */
public void run() {
if (debug) System.out.println(getName() + " starting run()");
try {
ByteBuffer deflated = DeflateData(deflatedLength);
InflateData(deflated);
} catch (Throwable t) {
t.printStackTrace();
fail(getName() + " failed");
} finally {
synchronized (flaters) {
flaters.remove(this);
if (flaters.isEmpty()) {
flaters.notifyAll();
}
}
}
}
/** Returns a copy of {@code data} in deflated form. */
private ByteBuffer DeflateData(int length) {
Deflater deflater = new Deflater();
if (srcMode == 0) {
deflater.setInput(dataHeap.array());
} else if (srcMode == 1) {
deflater.setInput(dataHeap.duplicate());
} else {
assert srcMode == 2;
deflater.setInput(dataDirect.duplicate());
}
deflater.finish();
ByteBuffer out = dstMode == 2 ? ByteBuffer.allocateDirect(length) : ByteBuffer.allocate(length);
int deflated;
if (dstMode == 0) {
deflated = deflater.deflate(out.array(), 0, length);
out.position(deflated);
} else {
deflater.deflate(out);
}
out.flip();
return out;
}
/** Inflate a byte array, comparing it with {@code data} during
* inflation.
* @throws Exception if inflated bytes don't match {@code data}.
*/
private void InflateData(ByteBuffer bytes) throws Throwable {
Inflater inflater = new Inflater();
if (dstMode == 0) {
inflater.setInput(bytes.array(), 0, bytes.remaining());
} else {
inflater.setInput(bytes);
}
if (inflater.getRemaining() == 0) {
throw new Exception("Nothing to inflate (bytes=" + bytes + ")");
}
int len = 1024 * 8;
int offset = 0;
ByteBuffer buf = srcMode == 2 ? ByteBuffer.allocateDirect(len) : ByteBuffer.allocate(len);
while (inflater.getRemaining() > 0) {
buf.clear();
int inflated;
if (srcMode == 0) {
inflated = inflater.inflate(buf.array(), 0, buf.remaining());
} else {
inflated = inflater.inflate(buf);
}
if (inflated == 0) {
throw new Exception("Nothing inflated (dst=" + buf + ",offset=" + offset + ",rem=" + inflater.getRemaining() + ",srcMode="+srcMode+",dstMode="+dstMode+")");
}
validate(buf, offset, inflated);
offset += inflated;
}
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 5092263
* @summary GZIPInputStream o GZIPOutputStream === the identity stream
* @author Martin Buchholz
* @key randomness
*/
// To manually test for uncompressed streams larger than 2GB, do
// javac Accordion.java && java -server Accordion 3325349068
// javac Accordion.java && java -server Accordion 5470735564
import java.io.*;
import java.util.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class Accordion {
private static void readFully(InputStream s, byte[] buf) throws Throwable {
int pos = 0;
int n;
while ((n = s.read(buf, pos, buf.length-pos)) > 0)
pos += n;
if (pos != buf.length)
throw new Exception("Unexpected EOF");
}
private static volatile Throwable trouble;
public static void main(String[] args) throws Throwable {
if (args.length > 1)
throw new Exception("Usage: java Accordion [BYTES]");
final long bytes = args.length == 0 ? 10001 : Long.parseLong(args[0]);
final int bufsize = 1729;
final long count = bytes/bufsize;
final PipedOutputStream out = new PipedOutputStream();
final PipedInputStream in = new PipedInputStream(out);
final Random random = new Random();
final byte[] data = new byte[1729];
for (int i = 0; i < data.length; i++)
data[i] = (byte)random.nextInt(255);
System.out.println("count="+count);
Thread compressor = new Thread() { public void run() {
try (GZIPOutputStream s = new GZIPOutputStream(out)) {
for (long i = 0; i < count; i++)
s.write(data, 0, data.length);
} catch (Throwable t) { trouble = t; }}};
Thread uncompressor = new Thread() { public void run() {
try (GZIPInputStream s = new GZIPInputStream(in)) {
final byte[] maybeBytes = new byte[data.length];
for (long i = 0; i < count; i++) {
readFully(s, maybeBytes);
if (! Arrays.equals(data, maybeBytes))
throw new Exception("data corruption");
}
if (s.read(maybeBytes, 0, 1) > 0)
throw new Exception("Unexpected NON-EOF");
} catch (Throwable t) { trouble = t; }}};
compressor.start(); uncompressor.start();
compressor.join(); uncompressor.join();
if (trouble != null)
throw trouble;
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/*
* @test
* @summary basic API verification tests for GZIPInputStream
* @run junit BasicGZIPInputStreamTest
*/
public class BasicGZIPInputStreamTest {
private static Stream<Arguments> npeFromConstructors() {
return Stream.of(Arguments.of((Executable) () -> new GZIPInputStream(null)),
Arguments.of((Executable) () -> new GZIPInputStream(null, 1)));
}
/*
* Verifies that the GZIPInputStream constructors throw the expected NullPointerException
*/
@ParameterizedTest
@MethodSource("npeFromConstructors")
public void testNPEFromConstructors(final Executable constructor) {
Assertions.assertThrows(NullPointerException.class, constructor,
"GZIPInputStream constructor did not throw NullPointerException");
}
private static Stream<Arguments> iaeFromConstructors() {
return Stream.of(
Arguments.of((Executable) () -> new GZIPInputStream(
new ByteArrayInputStream(new byte[0]), 0)),
Arguments.of((Executable) () -> new GZIPInputStream(
new ByteArrayInputStream(new byte[0]), -1)),
Arguments.of((Executable) () -> new GZIPInputStream(
new ByteArrayInputStream(new byte[0]), -42)));
}
/*
* Verifies that the GZIPInputStream constructors throw the expected IllegalArgumentException
*/
@ParameterizedTest
@MethodSource("iaeFromConstructors")
public void testIAEFromConstructors(final Executable constructor) {
Assertions.assertThrows(IllegalArgumentException.class, constructor,
"GZIPInputStream constructor did not throw IllegalArgumentException");
}
private static Stream<Arguments> ioeFromConstructors() {
final ByteArrayInputStream notGZIPContent = new ByteArrayInputStream(new byte[0]);
return Stream.of(
Arguments.of((Executable) () -> new GZIPInputStream(notGZIPContent)),
Arguments.of((Executable) () -> new GZIPInputStream(
notGZIPContent, 1024 /* buffer size */)));
}
/*
* Verifies that the GZIPInputStream constructors throw the expected IOException
*/
@ParameterizedTest
@MethodSource("ioeFromConstructors")
public void testIOEFromConstructors(final Executable constructor) {
Assertions.assertThrows(IOException.class, constructor,
"GZIPInputStream constructor did not throw IOException");
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 7021870 8023431 8026756
* @summary Reading last gzip chain member must not close the input stream.
* Garbage following gzip entry must be ignored.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class GZIPInZip {
public static void main(String[] args)
throws Throwable {
doTest(false, false);
doTest(false, true);
doTest(true, false);
doTest(true, true);
}
private static void doTest(final boolean appendGarbage,
final boolean limitGISBuff)
throws Throwable {
byte[] buf;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos)) {
final byte[] xbuf = { 'x' };
zos.putNextEntry(new ZipEntry("a.gz"));
GZIPOutputStream gos1 = new GZIPOutputStream(zos);
gos1.write(xbuf);
gos1.finish();
if (appendGarbage)
zos.write(xbuf);
zos.closeEntry();
zos.putNextEntry(new ZipEntry("b.gz"));
GZIPOutputStream gos2 = new GZIPOutputStream(zos);
gos2.write(xbuf);
gos2.finish();
zos.closeEntry();
zos.flush();
buf = baos.toByteArray();
}
try (ByteArrayInputStream bais = new ByteArrayInputStream(buf);
ZipInputStream zis = new ZipInputStream(bais)) {
zis.getNextEntry();
GZIPInputStream gis1 = limitGISBuff ?
new GZIPInputStream(zis, 4) :
new GZIPInputStream(zis);
// try to read more than the entry has
gis1.skip(2);
try {
zis.getNextEntry();
} catch (IOException e) {
throw new RuntimeException("ZIP stream was prematurely closed", e);
}
}
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 7036144
* @summary Test concatenated gz streams when available() returns zero
* @run junit GZIPInputStreamAvailable
*/
import org.junit.jupiter.api.Test;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
public class GZIPInputStreamAvailable {
public static final int NUM_COPIES = 100;
@Test
public void testZeroAvailable() throws IOException {
// Create some uncompressed data and then repeat it NUM_COPIES times
byte[] uncompressed1 = "this is a test".getBytes("ASCII");
byte[] uncompressedN = repeat(uncompressed1, NUM_COPIES);
// Compress the original data and then repeat that NUM_COPIES times
byte[] compressed1 = deflate(uncompressed1);
byte[] compressedN = repeat(compressed1, NUM_COPIES);
// (a) Read back inflated data from a stream where available() is accurate and verify
byte[] readback1 = inflate(new ByteArrayInputStream(compressedN));
assertArrayEquals(uncompressedN, readback1);
// (b) Read back inflated data from a stream where available() always returns zero and verify
byte[] readback2 = inflate(new ZeroAvailableStream(new ByteArrayInputStream(compressedN)));
assertArrayEquals(uncompressedN, readback2);
}
public static byte[] repeat(byte[] data, int count) {
byte[] repeat = new byte[data.length * count];
int off = 0;
for (int i = 0; i < count; i++) {
System.arraycopy(data, 0, repeat, off, data.length);
off += data.length;
}
return repeat;
}
public static byte[] deflate(byte[] data) throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
try (GZIPOutputStream out = new GZIPOutputStream(buf)) {
out.write(data);
}
return buf.toByteArray();
}
public static byte[] inflate(InputStream in) throws IOException {
return new GZIPInputStream(in).readAllBytes();
}
public static class ZeroAvailableStream extends FilterInputStream {
public ZeroAvailableStream(InputStream in) {
super(in);
}
@Override
public int available() {
return 0;
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4691425
* @summary Test the read and write of GZIPInput/OutputStream, including
* concatenated .gz inputstream
* @key randomness
*/
import java.io.*;
import java.util.*;
import java.util.zip.*;
public class GZIPInputStreamRead {
public static void main(String[] args) throws Throwable {
Random rnd = new Random();
for (int i = 1; i < 100; i++) {
int members = rnd.nextInt(10) + 1;
ByteArrayOutputStream srcBAOS = new ByteArrayOutputStream();
ByteArrayOutputStream dstBAOS = new ByteArrayOutputStream();
for (int j = 0; j < members; j++) {
byte[] src = new byte[rnd.nextInt(8192) + 1];
rnd.nextBytes(src);
srcBAOS.write(src);
try (GZIPOutputStream gzos = new GZIPOutputStream(dstBAOS)) {
gzos.write(src);
}
}
byte[] srcBytes = srcBAOS.toByteArray();
byte[] dstBytes = dstBAOS.toByteArray();
// try different size of buffer to read the
// GZIPInputStream
/* just for fun when running manually
for (int j = 1; j < 10; j++) {
test(srcBytes, dstBytes, j);
}
*/
for (int j = 0; j < 10; j++) {
int readBufSZ = rnd.nextInt(2048) + 1;
test(srcBytes,
dstBytes,
readBufSZ,
512); // the defualt buffer size
test(srcBytes,
dstBytes,
readBufSZ,
rnd.nextInt(4096) + 1);
}
}
}
private static void test(byte[] src, byte[] dst,
int readBufSize, int gzisBufSize)
throws Throwable
{
try (ByteArrayInputStream bais = new ByteArrayInputStream(dst);
GZIPInputStream gzis = new GZIPInputStream(bais, gzisBufSize))
{
byte[] result = new byte[src.length + 10];
byte[] buf = new byte[readBufSize];
int n = 0;
int off = 0;
while ((n = gzis.read(buf, 0, buf.length)) != -1) {
System.arraycopy(buf, 0, result, off, n);
off += n;
// no range check, if overflow, let it fail
}
if (off != src.length || gzis.available() != 0 ||
!Arrays.equals(src, Arrays.copyOf(result, off))) {
throw new RuntimeException(
"GZIPInputStream reading failed! " +
", src.len=" + src.length +
", read=" + off);
}
}
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* @test
* @bug 8244706
* @summary Verify that the OS header flag in the stream written out by java.util.zip.GZIPOutputStream
* has the correct expected value
* @run junit GZIPOutputStreamHeaderTest
*/
public class GZIPOutputStreamHeaderTest {
private static final int OS_HEADER_INDEX = 9;
private static final int HEADER_VALUE_OS_UNKNOWN = 255;
/**
* Test that the {@code OS} header field in the GZIP output stream
* has a value of {@code 255} which represents "unknown"
*/
@Test
public void testOSHeader() throws Exception {
final String data = "Hello world!!!";
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (final GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos);) {
gzipOutputStream.write(data.getBytes(StandardCharsets.UTF_8));
}
final byte[] compressed = baos.toByteArray();
assertNotNull(compressed, "Compressed data is null");
assertEquals(HEADER_VALUE_OS_UNKNOWN, toUnsignedByte(compressed[OS_HEADER_INDEX]),
"Unexpected value for OS header");
// finally verify that the compressed data is readable back to the original
final String uncompressed;
try (final ByteArrayOutputStream os = new ByteArrayOutputStream();
final ByteArrayInputStream bis = new ByteArrayInputStream(compressed);
final GZIPInputStream gzipInputStream = new GZIPInputStream(bis)) {
gzipInputStream.transferTo(os);
uncompressed = new String(os.toByteArray(), StandardCharsets.UTF_8);
}
assertEquals(data, uncompressed, "Unexpected data read from GZIPInputStream");
}
private static int toUnsignedByte(final byte b) {
return b & 0xff;
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 6336960
* @summary Validate that GZIPInputStream.readUByte throws the correct
* exception when read() on the InputStream with which the GZIPInputStream was
* created returns an out-of-bounds value.
*/
import java.io.*;
import java.util.zip.GZIPInputStream;
public class ReadUByte {
/** Guaranteed to return a value that is out-of-spec for read(). */
static class BrokenInputStream extends ByteArrayInputStream {
BrokenInputStream() {
super(new byte[16]);
}
public int read() {
return -33;
}
}
public static void realMain(String[] args) throws Throwable {
try {
new GZIPInputStream(new BrokenInputStream());
fail("Failed to throw expected IOException");
} catch (IOException ex) {
String msg = ex.getMessage();
if (msg.indexOf("ReadUByte$BrokenInputStream.read() returned value out of range") < 0) {
fail("IOException contains incorrect message: '" + msg + "'");
}
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,500 @@
/*
* Copyright 2009 Google, Inc. All Rights Reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4206909 4813885 8191918
* @summary Test basic functionality of DeflaterOutputStream/InflaterInputStream
* and GZIPOutputStream/GZIPInputStream, including flush
* @key randomness
*/
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.*;
public class InflateIn_DeflateOut {
private static class PairedInputStream extends ByteArrayInputStream {
private PairedOutputStream out = null;
private Random random;
public PairedInputStream() {
// The ByteArrayInputStream needs to start with a buffer, but we
// need to set it to have no data
super(new byte[1]);
count = 0;
pos = 0;
random = new Random(new Date().getTime());
}
public void setPairedOutputStream(PairedOutputStream out) {
this.out = out;
}
private void maybeFlushPair() {
if (random.nextInt(100) < 10) {
out.flush();
}
}
public int read() {
maybeFlushPair();
return super.read();
}
public int read(byte b[], int off, int len) {
maybeFlushPair();
return super.read(b, off, len);
}
public void addBytes(byte[] bytes, int len) {
int oldavail = count - pos;
int newcount = oldavail + len;
byte[] newbuf = new byte[newcount];
System.arraycopy(buf, pos, newbuf, 0, oldavail);
System.arraycopy(bytes, 0, newbuf, oldavail, len);
pos = 0;
count = newcount;
buf = newbuf;
}
}
private static class PairedOutputStream extends ByteArrayOutputStream {
private PairedInputStream pairedStream = null;
public PairedOutputStream(PairedInputStream inputPair) {
super();
this.pairedStream = inputPair;
}
public void flush() {
if (count > 0) {
pairedStream.addBytes(buf, count);
reset();
}
}
public void close() {
flush();
}
}
private static boolean readFully(InputStream in, byte[] buf, int length)
throws IOException {
int pos = 0;
int n;
while ((n = in.read(buf, pos, length - pos)) > 0) {
pos += n;
if (pos == length) return true;
}
return false;
}
private static boolean readLineIfAvailable(InputStream in, StringBuilder sb)
throws IOException {
try {
while (in.available() > 0) {
int i = in.read();
if (i < 0) break;
char c = (char) (((byte) i) & 0xff);
sb.append(c);
if (c == '\n') return true;
}
} catch (EOFException e) {
// empty
}
return false;
}
/** Check that written, closed and read */
private static void WriteCloseRead() throws Throwable {
Random random = new Random(new Date().getTime());
PairedInputStream pis = new PairedInputStream();
InflaterInputStream iis = new InflaterInputStream(pis);
PairedOutputStream pos = new PairedOutputStream(pis);
pis.setPairedOutputStream(pos);
byte[] data = new byte[random.nextInt(1024 * 1024)];
byte[] buf = new byte[data.length];
random.nextBytes(data);
try (DeflaterOutputStream dos = new DeflaterOutputStream(pos, true)) {
dos.write(data);
}
check(readFully(iis, buf, buf.length));
check(Arrays.equals(data, buf));
}
private static void TestFlushableGZIPOutputStream() throws Throwable {
var random = new Random(new Date().getTime());
var byteOutStream = new ByteArrayOutputStream();
var output = new FlushableGZIPOutputStream(byteOutStream);
var data = new byte[random.nextInt(1024 * 1024)];
var buf = new byte[data.length];
random.nextBytes(data);
output.write(data);
for (int i=0; i<data.length; i++) {
output.write(data[i]);
}
output.flush();
for (int i=0; i<data.length; i++) {
output.write(data[i]);
}
output.write(data);
output.close();
var baos = new ByteArrayOutputStream();
try (var gzis = new GZIPInputStream(new
ByteArrayInputStream(byteOutStream.toByteArray()))) {
gzis.transferTo(baos);
}
var decompressedBytes = baos.toByteArray();
check(decompressedBytes.length == data.length * 4);
}
private static void check(InputStream is, OutputStream os)
throws Throwable
{
Random random = new Random(new Date().getTime());
// Large writes
for (int x = 0; x < 200 ; x++) {
// byte[] data = new byte[random.nextInt(1024 * 1024)];
byte[] data = new byte[1024];
byte[] buf = new byte[data.length];
random.nextBytes(data);
os.write(data);
os.flush();
check(readFully(is, buf, buf.length));
check(Arrays.equals(data, buf));
}
// Small writes
for (int x = 0; x < 2000 ; x++) {
byte[] data = new byte[random.nextInt(20) + 10];
byte[] buf = new byte[data.length];
random.nextBytes(data);
os.write(data);
os.flush();
if (!readFully(is, buf, buf.length)) {
fail("Didn't read full buffer of " + buf.length);
}
check(Arrays.equals(data, buf));
}
String quit = "QUIT\r\n";
// Close it out
os.write(quit.getBytes());
os.close();
StringBuilder sb = new StringBuilder();
check(readLineIfAvailable(is, sb));
equal(sb.toString(), quit);
}
/** Check that written, flushed and read */
private static void WriteFlushRead() throws Throwable {
PairedInputStream pis = new PairedInputStream();
InflaterInputStream iis = new InflaterInputStream(pis);
PairedOutputStream pos = new PairedOutputStream(pis);
pis.setPairedOutputStream(pos);
DeflaterOutputStream dos = new DeflaterOutputStream(pos, true);
check(iis, dos);
}
private static void GZWriteFlushRead() throws Throwable {
PairedInputStream pis = new PairedInputStream();
PairedOutputStream pos = new PairedOutputStream(pis);
pis.setPairedOutputStream(pos);
GZIPOutputStream gos = new GZIPOutputStream(pos, true);
gos.flush(); // flush the head out, so gis can read
GZIPInputStream gis = new GZIPInputStream(pis);
check(gis, gos);
}
private static void checkLOP(InputStream is, OutputStream os)
throws Throwable
{
boolean flushed = false;
int count = 0;
// Do at least a certain number of lines, but too many without a
// flush means this test isn't testing anything
while ((count < 10 && flushed) || (count < 1000 && !flushed)) {
String command = "PING " + count + "\r\n";
os.write(command.getBytes());
StringBuilder buf = new StringBuilder();
if (!readLineIfAvailable(is, buf)) {
flushed = true;
os.flush();
check(readLineIfAvailable(is, buf));
}
equal(buf.toString(), command);
count++;
}
check(flushed);
}
/** Validate that we need to use flush at least once on a line
* oriented protocol */
private static void LineOrientedProtocol() throws Throwable {
PairedInputStream pis = new PairedInputStream();
InflaterInputStream iis = new InflaterInputStream(pis);
PairedOutputStream pos = new PairedOutputStream(pis);
pis.setPairedOutputStream(pos);
DeflaterOutputStream dos = new DeflaterOutputStream(pos, true);
checkLOP(iis, dos);
}
private static void GZLineOrientedProtocol() throws Throwable {
PairedInputStream pis = new PairedInputStream();
PairedOutputStream pos = new PairedOutputStream(pis);
pis.setPairedOutputStream(pos);
GZIPOutputStream gos = new GZIPOutputStream(pos, true);
gos.flush(); // flush the head out, so gis can read
GZIPInputStream gis = new GZIPInputStream(pis);
checkLOP(gis, gos);
}
private static byte[] deflate(final byte[] original) {
final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream();
try (Deflater compressor = new Deflater()) {
compressor.setInput(original);
compressor.finish();
while (!compressor.finished()) {
byte[] tmpBuffer = new byte[1024];
int numCompressed = compressor.deflate(tmpBuffer);
compressedBaos.write(tmpBuffer, 0, numCompressed);
}
}
return compressedBaos.toByteArray();
}
/**
* Verifies that when a InflaterInputStream is constructed
* by passing a Inflater instance, then closing the InflaterInputStream
* will not close the passed Inflater instance.
*/
private static void inflaterInputStreamInflaterNotClosed() throws Throwable {
// some arbitrary content
final byte[] original = "foo".repeat(1024).getBytes(StandardCharsets.US_ASCII);
// deflate it
final byte[] deflated = deflate(original);
// run the InflaterInputStream tests
try (final Inflater infl = new Inflater()) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(deflated);
InflaterInputStream iis = new InflaterInputStream(bis, infl)) {
final byte[] inflated = iis.readAllBytes();
check(Arrays.equals(original, inflated));
}
// verify the inflater wasn't closed - reset() will throw IllegalStateException if
// the inflater is closed
infl.reset();
// repeat the test with the other constructor
try (ByteArrayInputStream bis = new ByteArrayInputStream(deflated);
InflaterInputStream iis = new InflaterInputStream(bis, infl, 1024)) {
final byte[] inflated = iis.readAllBytes();
check(Arrays.equals(original, inflated));
}
// verify the inflater wasn't closed - reset() will throw IllegalStateException if
// the inflater is closed
infl.reset();
}
}
/**
* Verifies that when a DeflaterOutputStream is constructed
* by passing a Deflater instance, then closing the DeflaterOutputStream
* will not close the passed Deflater instance.
*/
private static void deflaterOutputStreamDeflaterNotClosed() throws Throwable {
// some arbitrary content
final byte[] data = "bar".repeat(1024).getBytes(StandardCharsets.US_ASCII);
// run the InflaterInputStream tests
try (final Deflater def = new Deflater()) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(bos, def)) {
dos.write(data);
}
// verify the deflater wasn't closed - reset() will throw IllegalStateException if
// the deflater is closed
def.reset();
// repeat the test with the other constructor
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(bos, def, 1024)) {
dos.write(data);
}
// verify the deflater wasn't closed - reset() will throw IllegalStateException if
// the deflater is closed
def.reset();
// repeat the test with the other constructor
for (boolean syncFlush : new boolean[] {false, true}) {
System.out.println("testing with syncFlush = " + syncFlush);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(bos, def, syncFlush)) {
dos.write(data);
}
// verify the deflater wasn't closed - reset() will throw IllegalStateException if
// the deflater is closed
def.reset();
}
// repeat the test with the other constructor
for (boolean syncFlush : new boolean[] {false, true}) {
System.out.println("testing with syncFlush = " + syncFlush);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(bos, def, 1024, syncFlush)) {
dos.write(data);
}
// verify the deflater wasn't closed - reset() will throw IllegalStateException if
// the deflater is closed
def.reset();
}
}
}
public static void realMain(String[] args) throws Throwable {
WriteCloseRead();
WriteFlushRead();
LineOrientedProtocol();
GZWriteFlushRead();
GZLineOrientedProtocol();
TestFlushableGZIPOutputStream();
inflaterInputStreamInflaterNotClosed();
deflaterOutputStreamDeflaterNotClosed();
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}
class FlushableGZIPOutputStream extends GZIPOutputStream {
public FlushableGZIPOutputStream(OutputStream os) throws IOException {
super(os);
}
private static final byte[] EMPTYBYTEARRAY = new byte[0];
private boolean hasData = false;
/**
* Here we make sure we have received data, so that the header has been for
* sure written to the output stream already.
*/
@Override
public synchronized void write(byte[] bytes, int i, int i1)
throws IOException {
super.write(bytes, i, i1);
hasData = true;
}
@Override
public synchronized void write(int i) throws IOException {
super.write(i);
hasData = true;
}
@Override
public synchronized void write(byte[] bytes) throws IOException {
super.write(bytes);
hasData = true;
}
@Override
public synchronized void flush() throws IOException {
if (!hasData) {
return; // do not allow the gzip header to be flushed on its own
}
// trick the deflater to flush
/**
* Now this is tricky: We force the Deflater to flush its data by
* switching compression level. As yet, a perplexingly simple workaround
* for
* http://developer.java.sun.com/developer/bugParade/bugs/4255743.html
*/
if (!def.finished()) {
def.setInput(EMPTYBYTEARRAY, 0, 0);
def.setLevel(Deflater.NO_COMPRESSION);
deflate();
def.setLevel(Deflater.DEFAULT_COMPRESSION);
deflate();
out.flush();
}
hasData = false; // no more data to flush
}
/*
* Keep on calling deflate until it runs dry. The default implementation
* only does it once and can therefore hold onto data when they need to be
* flushed out.
*/
@Override
protected void deflate() throws IOException {
int len;
do {
len = def.deflate(buf, 0, buf.length);
if (len > 0) {
out.write(buf, 0, len);
}
} while (len != 0);
}
}

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 6571338
* @summary Inflater should not require a buffer to the inflate() methods
* larger than 1 byte.
* @key randomness
*/
import java.io.*;
import java.nio.*;
import java.util.*;
import java.util.zip.*;
public class InflaterBufferSize {
private static final int DATA_LEN = 1024 *64;
private static byte[] data;
// If true, print extra info.
private static final boolean debug = System.getProperty("debug") != null;
private static void debug(String s) {
if (debug) System.out.println(s);
}
private static void createData() {
ByteBuffer bb = ByteBuffer.allocate(8);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < DATA_LEN; i++) {
bb.putDouble(0, Math.random());
baos.write(bb.array(), 0, 8);
}
data = baos.toByteArray();
}
private static byte[] grow(byte[] a, int capacity) {
while (a.length < capacity) {
byte[] a2 = new byte[a.length * 2];
System.arraycopy(a, 0, a2, 0, a.length);
a = a2;
}
return a;
}
private static byte[] trim(byte[] a, int length) {
byte[] res = new byte[length];
System.arraycopy(a, 0, res, 0, length);
return res;
}
private static byte[] deflate(byte[] in, int level) throws Throwable {
final Deflater flater = new Deflater(level);
flater.setInput(in);
flater.finish();
final byte[] smallBuffer = new byte[32];
byte[] flated = new byte[32];
int count = 0;
int n;
while ((n = flater.deflate(smallBuffer)) > 0) {
flated = grow(flated, count + n);
System.arraycopy(smallBuffer, 0, flated, count, n);
count += n;
}
return trim(flated, count);
}
private static byte[] inflate(byte[] in) throws Throwable {
final Inflater flater = new Inflater();
flater.setInput(in);
// This is the buffer of interest. It should be possible to use any
// non-zero size.
final byte[] smallBuffer = new byte[1];
byte[] flated = new byte[32];
int count = 0;
int n;
while ((n = flater.inflate(smallBuffer)) > 0) {
flated = grow(flated, count + n);
System.arraycopy(smallBuffer, 0, flated, count, n);
count += n;
}
return trim(flated, count);
}
public static void realMain(String[] args) throws Throwable {
byte deflated[], inflated[];
int level = -1;
if (args.length > 0) {
level = Integer.parseInt(args[0]);
}
debug("Using level " + level);
if (args.length > 1) {
FileInputStream fis = new FileInputStream(args[1]);
int len = fis.available();
data = new byte[len];
check(fis.read(data, 0, len) == len, "Did not read complete file");
debug("Original data from " + args[1]);
fis.close();
} else {
createData();
debug("Original data from random byte array");
}
debug("Original data length: " + data.length + " bytes");
debug("");
deflated = deflate(data, level);
debug("Deflated data length: " + deflated.length + " bytes");
inflated = inflate(deflated);
debug("Inflated data length: "+ inflated.length + " bytes" );
if (!check(Arrays.equals(data, inflated),
"Inflated and deflated arrays do not match")) {
OutputStream os = new BufferedOutputStream(new FileOutputStream("deflated.zip"));
try {
os.write(deflated);
} finally {
os.close();
}
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void pass(String msg) {System.out.println(msg); passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void unexpected(Throwable t, String msg) {
System.out.println(msg); failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static boolean check(boolean cond, String msg) {if (cond) pass(); else fail(msg); return cond;}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,271 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import org.junit.jupiter.api.Test;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*
* @test
* @bug 8225763
* @summary Test that the close() and end() methods on java.util.zip.Inflater
* @run junit InflaterClose
*/
public class InflaterClose {
private static final String originalStr = "foobarhelloworld!!!!";
private static final byte[] originalBytes = originalStr.getBytes(US_ASCII);
private static final byte[] compressedData = compress();
/**
* Closes the Inflater multiple times and then expects close() and end() to be called that
* many times.
*/
@Test
public void testCloseMultipleTimes() throws Exception {
final int numTimes = 3;
final Inflater simpleInflater = new Inflater();
final String inflatedData = closeMultipleTimesAfterInflating(numTimes, simpleInflater);
assertValidInflatedData(inflatedData, simpleInflater.getClass());
final OverrideClose overriddenClose = new OverrideClose();
final String ocInflatedData = closeMultipleTimesAfterInflating(numTimes, overriddenClose);
assertValidInflatedData(ocInflatedData, overriddenClose.getClass());
// make sure close was called numTimes
assertEquals(numTimes, overriddenClose.numTimesCloseCalled, "close() was expected to be" +
" called " + numTimes + ", but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenClose.getClass().getName());
final OverrideEnd overriddenEnd = new OverrideEnd();
final String oeInflatedData = closeMultipleTimesAfterInflating(numTimes, overriddenEnd);
assertValidInflatedData(oeInflatedData, overriddenEnd.getClass());
// make sure end was called called numTimes
assertEquals(numTimes, overriddenEnd.numTimesEndCalled, "end() was expected to be called " +
numTimes + ", but was called " + overriddenEnd.numTimesEndCalled + " time(s) on " +
overriddenEnd.getClass().getName());
final OverrideCloseAndEnd overriddenCloseAndEnd = new OverrideCloseAndEnd();
final String oceInflatedData = closeMultipleTimesAfterInflating(numTimes,
overriddenCloseAndEnd);
assertValidInflatedData(oceInflatedData, overriddenCloseAndEnd.getClass());
// make sure end was called called numTimes
assertEquals(numTimes, overriddenCloseAndEnd.numTimesEndCalled, "end() was expected" +
" to be called " + numTimes + ", but was called " +
overriddenCloseAndEnd.numTimesEndCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
assertEquals(numTimes, overriddenCloseAndEnd.numTimesCloseCalled, "close() was expected" +
" to be called " + numTimes + ", but was called " +
overriddenCloseAndEnd.numTimesCloseCalled + " time(s) on " +
overriddenCloseAndEnd.getClass().getName());
}
/**
* Closes the Inflater first and then calls end(). Verifies that close() was called
* just once but end() was called twice (once internally through close() and once
* explicitly)
*/
@Test
public void testCloseThenEnd() throws Exception {
final Inflater simpleInflater = new Inflater();
final String inflatedData = inflateCloseThenEnd(simpleInflater);
assertValidInflatedData(inflatedData, simpleInflater.getClass());
final OverrideClose overriddenClose = new OverrideClose();
final String ocInflatedData = inflateCloseThenEnd(overriddenClose);
assertValidInflatedData(ocInflatedData, overriddenClose.getClass());
// make sure close was called once
assertEquals(1, overriddenClose.numTimesCloseCalled, "close() was expected to be called" +
" once, but was called " + overriddenClose.numTimesCloseCalled + " time(s) on "
+ overriddenClose.getClass().getName());
final OverrideEnd overriddenEnd = new OverrideEnd();
final String oeInflatedData = inflateCloseThenEnd(overriddenEnd);
assertValidInflatedData(oeInflatedData, overriddenEnd.getClass());
// make sure end was called twice (once through close() and then explicitly)
assertEquals(2, overriddenEnd.numTimesEndCalled, "end() was expected to be called twice," +
" but was called " + overriddenEnd.numTimesEndCalled + " time(s) on "
+ overriddenEnd.getClass().getName());
final OverrideCloseAndEnd overriddenCloseAndEnd = new OverrideCloseAndEnd();
final String oceInflatedData = inflateCloseThenEnd(overriddenCloseAndEnd);
assertValidInflatedData(oceInflatedData, overriddenCloseAndEnd.getClass());
// make sure end was called twice (once through close and once explicitly)
// and close was called once
assertEquals(2, overriddenCloseAndEnd.numTimesEndCalled, "end() was expected to be called" +
" twice, but was called " + overriddenCloseAndEnd.numTimesEndCalled
+ " time(s) on " + overriddenCloseAndEnd.getClass().getName());
assertEquals(1, overriddenCloseAndEnd.numTimesCloseCalled, "close() was expected to be" +
" called once, but was called " + overriddenClose.numTimesCloseCalled
+ " time(s) on " + overriddenCloseAndEnd.getClass().getName());
}
/**
* Calls end() on the Inflater first and then calls close(). Verifies that close() was called
* just once and end() twice.
*/
@Test
public void testEndThenClose() throws Exception {
final Inflater simpleInflater = new Inflater();
final String inflatedData = inflateThenEndThenClose(simpleInflater);
assertValidInflatedData(inflatedData, simpleInflater.getClass());
final OverrideClose overriddenClose = new OverrideClose();
final String ocInflatedData = inflateThenEndThenClose(overriddenClose);
assertValidInflatedData(ocInflatedData, overriddenClose.getClass());
// make sure close was called once
assertEquals(1, overriddenClose.numTimesCloseCalled, "close() was expected to be" +
" called once, but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenClose.getClass().getName());
final OverrideEnd overriddenEnd = new OverrideEnd();
final String oeInflatedData = inflateThenEndThenClose(overriddenEnd);
assertValidInflatedData(oeInflatedData, overriddenEnd.getClass());
// make sure end was called twice (once through the explicit end call and
// once through close())
assertEquals(2, overriddenEnd.numTimesEndCalled, "end() was expected to be called twice," +
" but was called " + overriddenEnd.numTimesEndCalled + " time(s) on "
+ overriddenEnd.getClass().getName());
final OverrideCloseAndEnd overriddenCloseAndEnd = new OverrideCloseAndEnd();
final String oceInflatedData = inflateThenEndThenClose(overriddenCloseAndEnd);
assertValidInflatedData(oceInflatedData, overriddenCloseAndEnd.getClass());
// make sure end was called twice (once through the explicit end call and
// once through close())
assertEquals(2, overriddenCloseAndEnd.numTimesEndCalled, "end() was expected to be called" +
" twice, but was called " + overriddenCloseAndEnd.numTimesEndCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
assertEquals(1, overriddenCloseAndEnd.numTimesCloseCalled, "close() was expected to be" +
" called once, but was called " + overriddenClose.numTimesCloseCalled +
" time(s) on " + overriddenCloseAndEnd.getClass().getName());
}
private String closeMultipleTimesAfterInflating(final int numTimes, final Inflater inflater)
throws DataFormatException {
// inflate() then call close() multiple times
final byte[] inflatedData = inflate(inflater, compressedData);
// call close()
for (int i = 0; i < numTimes; i++) {
inflater.close();
}
return new String(inflatedData, StandardCharsets.UTF_8);
}
private String inflateCloseThenEnd(final Inflater inflater) throws Exception {
final byte[] inflatedData;
// inflate then close() and then end()
try (final Inflater inflt = inflater) {
inflatedData = inflate(inflt, compressedData);
}
// end() the already closed inflater
inflater.end();
return new String(inflatedData, StandardCharsets.UTF_8);
}
private String inflateThenEndThenClose(final Inflater inflater) throws Exception {
final byte[] inflatedData;
// inflate then end() and then close()
try (final Inflater inflt = inflater) {
inflatedData = inflate(inflt, compressedData);
// end() it first before it's (auto)closed by the try-with-resources
inflt.end();
}
return new String(inflatedData, StandardCharsets.UTF_8);
}
private static byte[] inflate(final Inflater inflater, final byte[] compressedData)
throws DataFormatException {
final ByteArrayOutputStream inflatedData = new ByteArrayOutputStream();
inflater.setInput(compressedData);
while (!inflater.finished()) {
byte[] tmpBuffer = new byte[100];
final int numDecompressed = inflater.inflate(tmpBuffer);
inflatedData.write(tmpBuffer, 0, numDecompressed);
}
return inflatedData.toByteArray();
}
private static byte[] compress() {
final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream();
try (final Deflater deflater = new Deflater()) {
deflater.setInput(originalBytes);
deflater.finish();
while (!deflater.finished()) {
final byte[] tmpBuffer = new byte[100];
final int numCompressed = deflater.deflate(tmpBuffer);
compressedBaos.write(tmpBuffer, 0, numCompressed);
}
}
return compressedBaos.toByteArray();
}
private static void assertValidInflatedData(final String inflatedData,
final Class<?> inflaterType) {
assertEquals(originalStr, inflatedData, "Unexpected inflated data " + inflatedData
+ " generated by " + inflaterType.getName() + ", expected " + originalStr);
}
private static final class OverrideEnd extends Inflater {
private int numTimesEndCalled = 0;
@Override
public void end() {
this.numTimesEndCalled++;
super.end();
}
}
private static final class OverrideClose extends Inflater {
private int numTimesCloseCalled = 0;
@Override
public void close() {
this.numTimesCloseCalled++;
super.close();
}
}
private static final class OverrideCloseAndEnd extends Inflater {
private int numTimesEndCalled = 0;
private int numTimesCloseCalled = 0;
@Override
public void end() {
this.numTimesEndCalled++;
super.end();
}
@Override
public void close() {
this.numTimesCloseCalled++;
super.close();
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4812237
* @summary Make sure that the mark() and reset() methods behave as expected.
*/
import java.io.*;
import java.util.zip.*;
public class MarkReset {
public static void main(String args[]) throws Exception {
test1();
}
private static void test1() throws Exception {
boolean expOccurred = false;
byte data[] = {90, 91, 92, 93, 94, 95, 96, 97};
InflaterInputStream in =
new InflaterInputStream(new ByteArrayInputStream(data));
in.mark(-5);
in.mark(6);
in.mark(Integer.MAX_VALUE);
try {
in.reset();
throw new RuntimeException("Expected exception not thrown");
} catch (IOException e) {
// Correct result
}
try {
in.reset();
throw new RuntimeException("Expected exception not thrown");
} catch (IOException e) {
// Correct result
}
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main TestAvailable
* @bug 7031075 8161426
* @summary Make sure that available() method behaves as expected.
* @key randomness
*/
import java.io.*;
import java.util.Random;
import java.util.zip.*;
import jdk.test.lib.RandomFactory;
public class TestAvailable {
public static void main(String args[]) throws Throwable {
Random r = RandomFactory.getRandom();
for (int n = 0; n < 10; n++) {
byte[] src = new byte[r.nextInt(100) + 1];
r.nextBytes(src);
// test InflaterInputStream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DeflaterOutputStream dos = new DeflaterOutputStream(baos)) {
dos.write(src);
}
try (InflaterInputStream iis = new InflaterInputStream(
new ByteArrayInputStream(baos.toByteArray()))) {
test(iis, src);
}
// test GZIPInputStream
baos = new ByteArrayOutputStream();
try (GZIPOutputStream dos = new GZIPOutputStream(baos)) {
dos.write(src);
}
try (GZIPInputStream gis = new GZIPInputStream(
new ByteArrayInputStream(baos.toByteArray()))) {
test(gis, src);
}
}
}
private static void test(InputStream is, byte[] expected) throws IOException {
int cnt = 0;
do {
int available = is.available();
if (available > 0) {
int b = is.read();
if (b == -1) {
throw new RuntimeException("available() > 0, read() == -1 : failed!");
}
if (expected[cnt++] != (byte)b) {
throw new RuntimeException("read() : failed!");
}
} else if (available == 0) {
if (is.read() != -1) {
throw new RuntimeException("available() == 0, read() != -1 : failed!");
}
break;
} else {
throw new RuntimeException("available() < 0 : failed!");
}
} while (true);
if (cnt != expected.length) {
throw new RuntimeException("read : failed!");
}
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8292327
* @summary Test the case where Inflater.needsInput() is true but the native
* inflater still has unwritten output in its internal buffer.
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.zip.Deflater;
import java.util.zip.DeflaterInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
public class UnexpectedEndOfZlibStream {
public static void main(String[] args) throws Exception {
String original = "Readsuncompresseddataintoanarrayofbytes0123456789Ifcodelenis" +
"notzerothemethodwillblockReadsuncompresseddataintoanarrayofbytes123456789";
byte[] deflated =
new DeflaterInputStream(
new ByteArrayInputStream(original.getBytes("US-ASCII")),
new Deflater(Deflater.DEFAULT_COMPRESSION, /*nowrap=*/ true)
).readAllBytes();
// using readAllBytes succeeds, the underlying gzip data seems to be well-formed
byte[] inflated =
new InflaterInputStream(
new ByteArrayInputStream(deflated),
new Inflater(/*nowrap=*/ true)
).readAllBytes();
if (!original.equals(new String(inflated, "US-ASCII"))) {
throw new Exception("Inflated output differs from original");
}
// a 128 byte read followed by a 512 byte read fails after JDK-8281962
InflaterInputStream is =
new InflaterInputStream(
new ByteArrayInputStream(deflated),
new Inflater(/*nowrap=*/ true));
byte[] buf = new byte[512];
int n = is.read(buf, 0, 128);
int pos = n;
while (n > 0) {
if ((n = is.read(buf, pos, 1)) > 0) { // Unexpected end of ZLIB input stream
pos+= n;
}
}
if (!original.equals(new String(buf, 0, pos, "US-ASCII"))) {
throw new Exception("Inflated output differs from original");
}
}
}

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
/*
* @test
* @bug 8369181
* @summary Verify the behaviour of basic operations on InflaterOutputStream
* @run junit ${test.main.class}
*/
class InflaterOutputStreamTest {
private static final byte[] INPUT_CONTENT = "hello world".getBytes(US_ASCII);
private static byte[] COMPRESSED_CONTENT;
@BeforeAll
static void beforeAll() throws Exception {
// created the compressed content
final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream();
try (Deflater compressor = new Deflater()) {
compressor.setInput(INPUT_CONTENT);
compressor.finish();
while (!compressor.finished()) {
final byte[] tmpBuffer = new byte[100];
final int numCompressed = compressor.deflate(tmpBuffer);
compressedBaos.write(tmpBuffer, 0, numCompressed);
}
}
COMPRESSED_CONTENT = compressedBaos.toByteArray();
}
static List<Arguments> inflaterOutputStreams() {
final List<Arguments> args = new ArrayList<>();
args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream())));
args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream(),
new Inflater())));
args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream(),
new Inflater(), 1024)));
return args;
}
/*
* Verify that write() methods throw an IOException when the InflaterOutputStream
* is already closed.
*/
@ParameterizedTest
@MethodSource("inflaterOutputStreams")
void testWriteAfterClose(final InflaterOutputStream ios) throws Exception {
ios.close();
IOException ioe = assertThrows(IOException.class, () -> ios.write(COMPRESSED_CONTENT));
// verify it failed for right reason
assertStreamClosedIOE(ioe);
}
/*
* Verify that flush() throws an IOException when the InflaterOutputStream
* is already closed
*/
@ParameterizedTest
@MethodSource("inflaterOutputStreams")
void testFlushAfterClose(final InflaterOutputStream ios) throws Exception {
ios.close();
final IOException ioe = assertThrows(IOException.class, ios::flush);
// verify it failed for right reason
assertStreamClosedIOE(ioe);
}
/*
* Verify that finish() throws an IOException when the InflaterOutputStream
* is already closed.
*/
@ParameterizedTest
@MethodSource("inflaterOutputStreams")
void testFinishAfterClose(final InflaterOutputStream ios) throws Exception {
ios.close();
final IOException ioe = assertThrows(IOException.class, ios::finish);
// verify it failed for right reason
assertStreamClosedIOE(ioe);
}
/*
* Verify that after finish() is called on an InflaterOutputStream that was constructed
* without specifying a Inflater, any subsequent writes will throw an IOException.
*/
@Test
void testWriteAfterFinish() throws IOException {
// InflaterOutputStream that constructs an internal default Inflater
final InflaterOutputStream ios = new InflaterOutputStream(new ByteArrayOutputStream());
ios.finish();
final IOException ioe = assertThrows(IOException.class, () -> ios.write(COMPRESSED_CONTENT));
final String msg = ioe.getMessage();
// verify that it's the right IOException
if (msg == null || !msg.contains("Inflater closed")) {
// propagate the original exception
fail("unexpected exception message in IOException", ioe);
}
// repeat with a InflaterOutputStream that is passed a Inflater
try (Inflater intf = new Inflater();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InflaterOutputStream stream = new InflaterOutputStream(baos, intf)) {
stream.finish();
// not expected to throw any exception
stream.write(COMPRESSED_CONTENT);
stream.flush();
// verify the decompressed content is as expected
final byte[] decompressed = baos.toByteArray();
assertArrayEquals(INPUT_CONTENT, decompressed, "unexpected decompressed content");
}
}
private void assertStreamClosedIOE(final IOException ioe) {
final String msg = ioe.getMessage();
if (msg == null || !msg.contains("Stream closed")) {
// propagate the original exception
fail("unexpected exception message in IOException", ioe);
}
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 6237956
* @summary We must be able to read zip files created by Unix Info-Zip
* @author Martin Buchholz
*/
import java.util.*;
import java.util.zip.*;
import java.io.*;
public class InfoZip {
static int passed = 0, failed = 0;
static void fail(String msg) {
failed++;
new Exception(msg).printStackTrace();
}
static void unexpected(Throwable t) {
failed++;
t.printStackTrace();
}
static void check(boolean condition, String msg) {
if (! condition)
fail(msg);
}
static void check(boolean condition) {
check(condition, "Something's wrong");
}
private static String contents(ZipFile zf, ZipEntry ze) throws Exception {
InputStream is = zf.getInputStream(ze);
String result = contents(is);
is.close();
return result;
}
private static String contents(InputStream is) throws Exception {
StringBuilder sb = new StringBuilder();
int c;
while ((c = is.read()) != -1)
sb.append((char)c);
return sb.toString();
}
private static void checkZipEntry(ZipEntry ze, String contents) {
check(ze.getName().equals("someFile"), "filename");
check(ze.getExtra() != null, "extra");
check(contents.equals("Message in a Bottle\n"), "contents");
check(ze.getSize() == "Message in a Bottle\n".length());
}
public static void main(String[] args) throws Exception {
//----------------------------------------------------------------
// The file InfoZip.zip was created using Unix Info-Zip's zip, thus:
// echo Message in a Bottle > someFile; zip InfoZip.zip someFile
// Such a file has a LOC extra different from the CEN extra,
// which cannot happen using JDK APIs.
//----------------------------------------------------------------
File f = new File("InfoZip.zip");
try (OutputStream os = new FileOutputStream(f)) {
os.write(new byte[]
{'P', 'K', 3, 4, 10, 0, 0, 0, 0, 0, -68, 8, 'k',
'2', 'V', -7, 'm', 9, 20, 0, 0, 0, 20, 0, 0, 0,
8, 0, 21, 0, 's', 'o', 'm', 'e', 'F', 'i', 'l', 'e', 'U',
'T', 9, 0, 3, 't', '_', '1', 'B', 't', '_', '1', 'B', 'U',
'x', 4, 0, -14, 'v', 26, 4, 'M', 'e', 's', 's', 'a', 'g',
'e', ' ', 'i', 'n', ' ', 'a', ' ', 'B', 'o', 't', 't', 'l', 'e',
10, 'P', 'K', 1, 2, 23, 3, 10, 0, 0, 0, 0, 0,
-68, 8, 'k', '2', 'V', -7, 'm', 9, 20, 0, 0, 0, 20,
0, 0, 0, 8, 0, 13, 0, 0, 0, 0, 0, 1, 0,
0, 0, -92, -127, 0, 0, 0, 0, 's', 'o', 'm', 'e', 'F',
'i', 'l', 'e', 'U', 'T', 5, 0, 3, 't', '_', '1', 'B', 'U',
'x', 0, 0, 'P', 'K', 5, 6, 0, 0, 0, 0, 1, 0,
1, 0, 'C', 0, 0, 0, 'O', 0, 0, 0, 0, 0, });
}
ZipEntry ze = null;
try (ZipFile zf = new ZipFile(f)) {
Enumeration<? extends ZipEntry> entries = zf.entries();
ze = entries.nextElement();
check(! entries.hasMoreElements());
checkZipEntry(ze, contents(zf, ze));
}
try (FileInputStream fis = new FileInputStream(f);
ZipInputStream is = new ZipInputStream(fis))
{
ze = is.getNextEntry();
checkZipEntry(ze, contents(is));
check(is.getNextEntry() == null);
}
f.delete();
System.out.printf("passed = %d, failed = %d%n", passed, failed);
if (failed > 0) throw new Exception("Some tests failed");
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 6304463
* @summary Test whether the zip file still can be read after thread is interrupted
*/
import java.io.*;
import java.util.zip.*;
public class InterruptibleZip {
public static void main(String[] args) throws Exception {
/* Interrupt the current thread. The is.read call below
should continue reading input.jar.
*/
Thread.currentThread().interrupt();
ZipFile zf = new ZipFile(new File(System.getProperty("test.src", "."), "input.jar"));
ZipEntry ze = zf.getEntry("Available.java");
InputStream is = zf.getInputStream(ze);
byte[] buf = new byte[512];
int n = is.read(buf);
boolean interrupted = Thread.interrupted();
System.out.printf("interrupted=%s n=%d name=%s%n",
interrupted, n, ze.getName());
if (! interrupted) {
throw new Error("Wrong interrupted status");
}
if (n != buf.length) {
throw new Error("Read error");
}
}
}

View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.*;
import java.nio.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.nio.file.spi.*;
import java.util.*;
import java.util.zip.*;
import static java.nio.file.StandardCopyOption.*;
public class LargeZip {
// If true, don't delete large ZIP file created for test.
static final boolean debug = System.getProperty("debug") != null;
//static final int DATA_LEN = 1024 * 1024;
static final int DATA_LEN = 80 * 1024;
static final int DATA_SIZE = 8;
static long fileSize = 6L * 1024L * 1024L * 1024L; // 6GB
static boolean userFile = false;
static byte[] data;
static File largeFile;
static String lastEntryName;
/* args can be empty, in which case check a 3 GB file which is created for
* this test (and then deleted). Or it can be a number, in which case
* that designates the size of the file that's created for this test (and
* then deleted). Or it can be the name of a file to use for the test, in
* which case it is *not* deleted. Note that in this last case, the data
* comparison might fail.
*/
static void realMain (String[] args) throws Throwable {
if (args.length > 0) {
try {
fileSize = Long.parseLong(args[0]);
System.out.println("Testing with file of size " + fileSize);
} catch (NumberFormatException ex) {
largeFile = new File(args[0]);
if (!largeFile.exists()) {
throw new Exception("Specified file " + args[0] + " does not exist");
}
userFile = true;
System.out.println("Testing with user-provided file " + largeFile);
}
}
File testDir = null;
if (largeFile == null) {
testDir = new File(System.getProperty("test.scratch", "."),
"LargeZip");
if (testDir.exists()) {
if (!testDir.delete()) {
throw new Exception("Cannot delete already-existing test directory");
}
}
check(!testDir.exists() && testDir.mkdirs());
largeFile = new File(testDir, "largezip.zip");
createLargeZip();
} else {
if (args.length > 1)
updateLargeZip(args[1]); // add new entry with zfs
}
readLargeZip1();
readLargeZip2();
if (!userFile && !debug) {
check(largeFile.delete());
check(testDir.delete());
}
}
static void createLargeZip() throws Throwable {
int iterations = DATA_LEN / DATA_SIZE;
ByteBuffer bb = ByteBuffer.allocate(DATA_SIZE);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < iterations; i++) {
bb.putDouble(0, Math.random());
baos.write(bb.array(), 0, DATA_SIZE);
}
data = baos.toByteArray();
try (FileOutputStream fos = new FileOutputStream(largeFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos))
{
long length = 0;
while (length < fileSize) {
ZipEntry ze = new ZipEntry("entry-" + length);
lastEntryName = ze.getName();
zos.putNextEntry(ze);
zos.write(data, 0, data.length);
zos.closeEntry();
length = largeFile.length();
}
System.out.println("Last entry written is " + lastEntryName);
}
}
private static byte buf[] = new byte[4096];
static void checkEntry(ZipEntry e, InputStream is) throws Throwable {
long N = 0;
int n = 0;
while ((n = is.read(buf)) >= 0) {
N += n;
}
check(N == e.getSize());
}
static void readLargeZip1() throws Throwable {
ZipFile zipFile = new ZipFile(largeFile);
ZipEntry entry = null;
String entryName = null;
int count = 0;
System.out.println("ZipFile:");
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
entry = entries.nextElement();
entryName = entry.getName();
System.out.println(" checking " + entryName);
if (!entry.isDirectory()) {
try (InputStream zeis = zipFile.getInputStream(entry)) {
checkEntry(entry, zeis);
}
}
count++;
}
System.out.println("Number of entries read: " + count);
check(!entry.isDirectory());
if (userFile || check(entryName.equals(lastEntryName))) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = zipFile.getInputStream(entry);
int len;
while ((len = is.read(buf)) >= 0) {
baos.write(buf, 0, len);
}
baos.close();
is.close();
if (!userFile)
check(Arrays.equals(data, baos.toByteArray()));
}
}
static void readLargeZip2() throws Throwable {
System.out.println("ZipInputStream:");
try (FileInputStream fis = new FileInputStream(largeFile);
BufferedInputStream bis = new BufferedInputStream(fis);
ZipInputStream zis = new ZipInputStream(bis))
{
ZipEntry entry = null;
String entryName = null;
int count = 0;
while ((entry = zis.getNextEntry()) != null) {
entryName = entry.getName();
System.out.println(" checking " + entryName +
", method=" + entry.getMethod());
if (entryName.equals(lastEntryName)) {
break;
}
if (!entry.isDirectory()) {
checkEntry(entry, zis);
}
count++;
}
System.out.println("Number of entries read: " + count);
System.out.println("Last entry read is " + entryName);
if (!userFile) {
check(!entry.isDirectory());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buf[] = new byte[4096];
int len;
while ((len = zis.read(buf)) >= 0) {
baos.write(buf, 0, len);
}
baos.close();
check(Arrays.equals(data, baos.toByteArray()));
check(zis.getNextEntry() == null);
}
}
}
private static void updateFile(FileSystem fs, Path src) throws IOException {
Path dst = fs.getPath(src.toString());
Path parent = dst.getParent();
if (parent != null && Files.notExists(parent))
Files.createDirectories(parent);
Files.copy(src, dst, REPLACE_EXISTING);
}
private static FileSystemProvider getZipFSProvider() {
for (FileSystemProvider provider : FileSystemProvider.installedProviders()) {
if ("jar".equalsIgnoreCase(provider.getScheme()))
return provider;
}
return null;
}
static void updateLargeZip(String pName) throws Throwable {
FileSystemProvider provider = getZipFSProvider();
if (provider == null) {
System.err.println("ZIP filesystem provider is not installed");
System.exit(1);
}
Map<String, Object> env = env = new HashMap<>();
try (FileSystem fs = provider.newFileSystem(largeFile.toPath(), env)) {
Path path = FileSystems.getDefault().getPath(pName);
Files.walkFileTree(
path,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs)
throws IOException
{
updateFile(fs, file);
return FileVisitResult.CONTINUE;
}
});
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void pass(String msg) {System.out.println(msg); passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void unexpected(Throwable t, String msg) {
System.out.println(msg); failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4766057
* @summary Test for VM crash in getEntry
*/
import java.util.zip.*;
import java.util.*;
import java.io.*;
public class ReadLoc {
public static void main(String[] args) throws Exception {
int iterations = 0;
File zFile1 = new File(System.getProperty("test.src", "."),
"pkware123456789012345.zip");
while (iterations < 2500) {
ZipFile zipFile = new ZipFile(zFile1);
List entries = Collections.list(zipFile.entries());
for (Iterator it = entries.iterator(); it.hasNext();) {
ZipEntry zipEntry = (ZipEntry)it.next();
InputStream in = zipFile.getInputStream(zipEntry);
in.close();
}
iterations++;
}
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4879507
* @summary ZipInputStream does not check CRC for stored (uncompressed) files
* @author Dave Bristor
*/
import java.io.*;
import java.util.Arrays;
import java.util.zip.*;
public class StoredCRC {
public static void realMain(String[] args) throws Throwable {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry("test");
ze.setMethod(ZipOutputStream.STORED);
String writtenString = "hello, world";
byte[] writtenData = writtenString.getBytes("ASCII");
ze.setSize(writtenData.length);
CRC32 crc = new CRC32();
crc.update(writtenData);
ze.setCrc(crc.getValue());
zos.putNextEntry(ze);
zos.write(writtenData, 0, writtenData.length);
zos.close();
byte[] data = baos.toByteArray();
// Run with an arg to create a test file that can be used to figure
// out what position in the data stream can be changed.
if (args.length > 0) {
FileOutputStream fos = new FileOutputStream("stored.zip");
fos.write(data, 0, data.length);
fos.close();
} else {
// Test that reading byte-at-a-time works
ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(data));
ze = zis.getNextEntry();
int pos = 0;
byte[] readData = new byte[256];
try {
int count = zis.read(readData, pos, 1);
while (count > 0) {
count = zis.read(readData, ++pos, 1);
}
check(writtenString.equals(new String(readData, 0, pos, "ASCII")));
} catch (Throwable t) {
unexpected(t);
}
// Test that data corruption is detected. "offset" was
// determined to be in the entry's uncompressed data.
data[getDataOffset(data) + 4] ^= 1;
zis = new ZipInputStream(
new ByteArrayInputStream(data));
ze = zis.getNextEntry();
try {
zis.read(readData, 0, readData.length);
fail("Did not catch expected ZipException" );
} catch (ZipException ex) {
String msg = ex.getMessage();
check(msg != null && msg.startsWith("invalid entry CRC (expected 0x"));
} catch (Throwable t) {
unexpected(t);
}
}
}
public static final int getDataOffset(byte b[]) {
final int LOCHDR = 30; // LOC header size
final int LOCEXT = 28; // extra field length
final int LOCNAM = 26; // filename length
int lenExt = Byte.toUnsignedInt(b[LOCEXT]) | (Byte.toUnsignedInt(b[LOCEXT + 1]) << 8);
int lenNam = Byte.toUnsignedInt(b[LOCNAM]) | (Byte.toUnsignedInt(b[LOCNAM + 1]) << 8);
return LOCHDR + lenExt + lenNam;
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static boolean pass() {passed++; return true;}
static boolean fail() {failed++; Thread.dumpStack(); return false;}
static boolean fail(String msg) {System.out.println(msg); return fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static boolean equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) return pass();
else return fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4111750 4114745
@summary test if Deflater/Inflater constructor will
check the arguments correctly.
*/
import java.util.zip.*;
import java.io.*;
public class StreamConstructor {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Deflater def = new Deflater();
ByteArrayInputStream bis = new ByteArrayInputStream(new byte[10]);
Inflater inf = new Inflater();
InflaterInputStream infOS;
DeflaterOutputStream defOS;
try {
defOS = new DeflaterOutputStream(bos, null);
throw new Exception("didn't catch illegal argument");
} catch (NullPointerException e){
}
try {
defOS = new DeflaterOutputStream(null, def);
throw new Exception("didn't catch illegal argument");
} catch (NullPointerException e){
}
try {
defOS = new DeflaterOutputStream(bos, def, -1);
throw new Exception("didn't catch illegal argument");
} catch (IllegalArgumentException e) {
}
try {
infOS = new InflaterInputStream(bis, null);
throw new Exception("didn't catch illegal argument");
} catch (NullPointerException e){
}
try {
infOS = new InflaterInputStream(null, inf);
throw new Exception("didn't catch illegal argument");
} catch (NullPointerException e){
}
try {
infOS = new InflaterInputStream(bis, inf, -1);
throw new Exception("didn't catch illegal argument");
} catch (IllegalArgumentException e) {
}
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4110528 4112112 4112103
@summary Test if zip related in/output streams will
prevent i/o after stream has been closed.
*/
import java.util.zip.*;
import java.util.jar.*;
import java.io.*;
public class StreamIOAfterClose {
// compressed stub data
private static byte[] compressed = {
31,-117,8,0,0,0,0,0,0,0,-85,-107,-79,74,85,97,117,48,
-9,117,-47,114,15,-87,-27,-9,-54,-48,49,-108,17,19,20,
118,-87,-84,78,-15,-10,-87,-112,51,115,16,85,81,54,11,
114,44,11,98,116,17,102,-10,72,-10,80,-79,101,14,47,-50,
16,117,-9,-83,16,13,-55,83,83,103,-30,-117,-42,-82,-105,
-119,46,-16,20,-111,-85,-16,-48,54,79,-53,-76,116,
-80,-78,77,-88,-85,50,113,-54,15,-74,-28,-44,101,-43,47,
85,54,-74,1,0,85,69,28,117,100,0,0,0
};
private static void testRead(InputStream in) throws Exception {
in.close();
try {
in.read();
throw new Exception("read allowed after stream is closed");
} catch (IOException e) {
}
}
private static void testWrite(ZipOutputStream out) throws Exception {
out.close();
try {
out.putNextEntry(new ZipEntry(""));
throw new Exception("write allowed after stream is closed");
} catch (IOException e) {
}
}
public static void main(String argv[]) throws Exception {
ZipOutputStream zos = new ZipOutputStream(new ByteArrayOutputStream());
zos.putNextEntry(new ZipEntry("1"));
testWrite(zos);
JarOutputStream jos = new JarOutputStream(new ByteArrayOutputStream());
jos.putNextEntry(new ZipEntry("1"));
testWrite(jos);
InputStream bis = new ByteArrayInputStream(new byte[10]);
InputStream bis1 = new ByteArrayInputStream(compressed);
testRead(new ZipInputStream(bis));
testRead(new GZIPInputStream(bis1));
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.zip.CRC32;
/**
* @test @summary Check that CRC-32 returns the expected CRC value for the
* string 123456789
* http://reveng.sourceforge.net/crc-catalogue/all.htm#crc.cat.crc-32
* @build ChecksumBase
* @run main TestCRC32
*/
public class TestCRC32 {
public static void main(String[] args) {
ChecksumBase.testAll(new CRC32(), 0xCBF43926L);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.zip.CRC32C;
/**
* @test @summary Check that CRC-32C returns the expected CRC value for the
* string 123456789
* http://reveng.sourceforge.net/crc-catalogue/all.htm#crc.cat.crc-32c
* @build ChecksumBase
* @run main TestCRC32C
*/
public class TestCRC32C {
public static void main(String[] args) {
ChecksumBase.testAll(new CRC32C(), 0xE3069283L);
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test @summary Test that default methods in Checksum works as expected
* @build ChecksumBase
* @run main TestChecksum
*/
import java.util.zip.CRC32C;
import java.util.zip.Checksum;
public class TestChecksum {
public static void main(String[] args) {
ChecksumBase.testAll(new MyCRC32C(), 0xE3069283L);
}
/**
* Only implementing required methods
*/
private static class MyCRC32C implements Checksum {
private final CRC32C crc32c = new CRC32C();
@Override
public void update(int b) {
crc32c.update(b);
}
@Override
public void update(byte[] b, int off, int len) {
crc32c.update(b, off, len);
}
@Override
public long getValue() {
return crc32c.getValue();
}
@Override
public void reset() {
crc32c.reset();
}
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 6334003 6440786
* @summary Test ability to write and read zip files that have no entries.
* @author Dave Bristor
*/
import java.io.*;
import java.util.*;
import java.util.zip.*;
public class TestEmptyZip {
public static void realMain(String[] args) throws Throwable {
String zipName = "foo.zip";
File f = new File(System.getProperty("test.scratch", "."), zipName);
if (f.exists() && !f.delete()) {
throw new Exception("failed to delete " + zipName);
}
f.createNewFile();
try {
// Verify 0-length file cannot be read
checkCannotRead(f);
// Verify non-zip file cannot be read
OutputStream out = new FileOutputStream(f);
try {
out.write("class Foo { }".getBytes());
} finally {
out.close();
}
checkCannotRead(f);
} finally {
f.delete();
}
// Verify 0-entries file can be written
write(f);
// Verify 0-entries file can be read
readFile(f);
readStream(f);
f.delete();
}
static void checkCannotRead(File f) throws IOException {
try {
new ZipFile(f).close();
fail();
} catch (ZipException ze) {
if (f.length() == 0) {
check(ze.getMessage().contains("zip file is empty"));
} else {
pass();
}
}
try (FileInputStream fis = new FileInputStream(f);
ZipInputStream zis = new ZipInputStream(fis))
{
ZipEntry ze = zis.getNextEntry();
check(ze == null);
} catch (IOException ex) {
unexpected(ex);
}
}
static void write(File f) throws Exception {
try (FileOutputStream fis = new FileOutputStream(f);
ZipOutputStream zos = new ZipOutputStream(fis))
{
zos.finish();
pass();
} catch (Exception ex) {
unexpected(ex);
}
}
static void readFile(File f) throws Exception {
try (ZipFile zf = new ZipFile(f)) {
Enumeration e = zf.entries();
while (e.hasMoreElements()) {
ZipEntry entry = (ZipEntry) e.nextElement();
fail();
}
pass();
} catch (Exception ex) {
unexpected(ex);
}
}
static void readStream(File f) throws Exception {
try (FileInputStream fis = new FileInputStream(f);
ZipInputStream zis = new ZipInputStream(fis))
{
ZipEntry ze = zis.getNextEntry();
check(ze == null);
byte[] buf = new byte[1024];
check(zis.read(buf, 0, 1024) == -1);
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static boolean pass() {passed++; return true;}
static boolean fail() {failed++; Thread.dumpStack(); return false;}
static boolean fail(String msg) {System.out.println(msg); return fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static boolean equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) return pass();
else return fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,369 @@
/*
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641 8075526 8130914
* 8161942 8206389
* @summary Test ZOS and ZIS timestamp in extra field correctly
*/
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFilePermission;
import java.time.Instant;
import java.util.Arrays;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class TestExtraTime {
public static void main(String[] args) throws Throwable{
File src = new File(System.getProperty("test.src", "."), "TestExtraTime.java");
if (!src.exists()) {
return;
}
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
// A structurally valid extra data example
byte[] sampleExtra = new byte[Short.BYTES*3];
ByteBuffer.wrap(sampleExtra).order(ByteOrder.LITTLE_ENDIAN)
.putShort((short) 123) // ID: 123
.putShort((short) Short.BYTES) // Size: 2
.putShort((short) 42); // Data: Two bytes
for (byte[] extra : new byte[][] { null, sampleExtra}) {
// ms-dos 1980 epoch problem
test0(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra);
// negative epoch time
test0(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra);
long time = src.lastModified();
test(FileTime.from(time, TimeUnit.MILLISECONDS),
FileTime.from(time + 300000, TimeUnit.MILLISECONDS),
FileTime.from(time - 300000, TimeUnit.MILLISECONDS),
tz, extra);
// now
time = Instant.now().toEpochMilli();
test(FileTime.from(time, TimeUnit.MILLISECONDS),
FileTime.from(time + 300000, TimeUnit.MILLISECONDS),
FileTime.from(time - 300000, TimeUnit.MILLISECONDS),
tz, extra);
// unix 2038
time = 0x80000000L;
test(FileTime.from(time, TimeUnit.SECONDS),
FileTime.from(time, TimeUnit.SECONDS),
FileTime.from(time, TimeUnit.SECONDS),
tz, extra);
// mtime < unix 2038
time = 0x7fffffffL;
test(FileTime.from(time, TimeUnit.SECONDS),
FileTime.from(time + 30000, TimeUnit.SECONDS),
FileTime.from(time + 30000, TimeUnit.SECONDS),
tz, extra);
}
testNullHandling();
testTagOnlyHandling();
testTimeConversions();
testNullMtime();
}
static void test(FileTime mtime, FileTime atime, FileTime ctime,
TimeZone tz, byte[] extra) throws Throwable {
test0(mtime, null, null, null, extra);
test0(mtime, null, null, tz, extra); // non-default tz
test0(mtime, atime, null, null, extra);
test0(mtime, null, ctime, null, extra);
test0(mtime, atime, ctime, null, extra);
test0(mtime, atime, null, tz, extra);
test0(mtime, null, ctime, tz, extra);
test0(mtime, atime, ctime, tz, extra);
}
static void test0(FileTime mtime, FileTime atime, FileTime ctime,
TimeZone tz, byte[] extra) throws Throwable {
System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n",
mtime, atime, ctime);
TimeZone tz0 = TimeZone.getDefault();
if (tz != null) {
TimeZone.setDefault(tz);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry("TestExtraTime.java");
ze.setExtra(extra);
ze.setLastModifiedTime(mtime);
if (atime != null)
ze.setLastAccessTime(atime);
if (ctime != null)
ze.setCreationTime(ctime);
zos.putNextEntry(ze);
zos.write(new byte[] { 1,2 ,3, 4});
// append an extra entry to help check if the length and data
// of the extra field are being correctly written (in previous
// entry).
if (extra != null) {
ze = new ZipEntry("TestExtraEntry");
zos.putNextEntry(ze);
}
zos.close();
if (tz != null) {
TimeZone.setDefault(tz0);
}
// ZipInputStream
ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()));
ze = zis.getNextEntry();
zis.close();
check(mtime, atime, ctime, ze, extra);
// ZipFile
Path zpath = Paths.get(System.getProperty("test.dir", "."),
"TestExtraTime.zip");
Path zparent = zpath.getParent();
if (zparent != null && !Files.isWritable(zparent)) {
System.err.format("zpath %s parent %s is not writable%n",
zpath, zparent);
}
if (Files.exists(zpath)) {
System.err.format("zpath %s already exists%n", zpath);
if (Files.isDirectory(zpath)) {
System.err.format("%n%s contents:%n", zpath);
Files.list(zpath).forEach(System.err::println);
}
FileOwnerAttributeView foav = Files.getFileAttributeView(zpath,
FileOwnerAttributeView.class);
System.err.format("zpath %s owner: %s%n", zpath, foav.getOwner());
System.err.format("zpath %s permissions:%n", zpath);
Set<PosixFilePermission> perms =
Files.getPosixFilePermissions(zpath);
perms.stream().forEach(System.err::println);
}
if (Files.isSymbolicLink(zpath)) {
System.err.format("zpath %s is a symbolic link%n", zpath);
}
Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
try (ZipFile zf = new ZipFile(zpath.toFile())) {
ze = zf.getEntry("TestExtraTime.java");
// ZipFile read entry from cen, which does not have a/ctime,
// for now.
check(mtime, null, null, ze, extra);
} finally {
Files.delete(zpath);
}
}
static void check(FileTime mtime, FileTime atime, FileTime ctime,
ZipEntry ze, byte[] extra) {
/*
System.out.printf(" mtime [%tc]: [%tc]/[%tc]%n",
mtime.to(TimeUnit.MILLISECONDS),
ze.getTime(),
ze.getLastModifiedTime().to(TimeUnit.MILLISECONDS));
*/
if (mtime != null &&
mtime.to(TimeUnit.SECONDS) !=
ze.getLastModifiedTime().to(TimeUnit.SECONDS)) {
throw new RuntimeException("Timestamp: storing mtime failed!");
}
if (atime != null &&
atime.to(TimeUnit.SECONDS) !=
ze.getLastAccessTime().to(TimeUnit.SECONDS))
throw new RuntimeException("Timestamp: storing atime failed!");
if (ctime != null &&
ctime.to(TimeUnit.SECONDS) !=
ze.getCreationTime().to(TimeUnit.SECONDS))
throw new RuntimeException("Timestamp: storing ctime failed!");
if (extra != null) {
// if extra data exists, the current implementation put it at
// the end of the extra data array (implementation detail)
byte[] extra1 = ze.getExtra();
if (extra1 == null || extra1.length < extra.length ||
!Arrays.equals(Arrays.copyOfRange(extra1,
extra1.length - extra.length,
extra1.length),
extra)) {
throw new RuntimeException("Timestamp: storing extra field failed!");
}
}
}
static void testNullHandling() {
ZipEntry ze = new ZipEntry("TestExtraTime.java");
try {
ze.setLastAccessTime(null);
throw new RuntimeException("setLastAccessTime(null) should throw NPE");
} catch (NullPointerException ignored) {
// pass
}
try {
ze.setCreationTime(null);
throw new RuntimeException("setCreationTime(null) should throw NPE");
} catch (NullPointerException ignored) {
// pass
}
try {
ze.setLastModifiedTime(null);
throw new RuntimeException("setLastModifiedTime(null) should throw NPE");
} catch (NullPointerException ignored) {
// pass
}
}
// verify that setting and getting any time is possible as per the intent
// of 4759491
static void testTimeConversions() {
// Sample across the entire range
long step = Long.MAX_VALUE / 100L;
testTimeConversions(Long.MIN_VALUE, Long.MAX_VALUE - step, step);
// Samples through the near future
long currentTime = System.currentTimeMillis();
testTimeConversions(currentTime, currentTime + 1_000_000, 10_000);
}
static void testTimeConversions(long from, long to, long step) {
ZipEntry ze = new ZipEntry("TestExtraTime.java");
for (long time = from; time <= to; time += step) {
ze.setTime(time);
FileTime lastModifiedTime = ze.getLastModifiedTime();
if (lastModifiedTime.toMillis() != time) {
throw new RuntimeException("setTime should make getLastModifiedTime " +
"return the specified instant: " + time +
" got: " + lastModifiedTime.toMillis());
}
if (ze.getTime() != time) {
throw new RuntimeException("getTime after setTime, expected: " +
time + " got: " + ze.getTime());
}
}
}
static void check(ZipEntry ze, byte[] extra) {
if (extra != null) {
byte[] extra1 = ze.getExtra();
if (extra1 == null || extra1.length < extra.length ||
!Arrays.equals(Arrays.copyOfRange(extra1,
extra1.length - extra.length,
extra1.length),
extra)) {
throw new RuntimeException("Timestamp: storing extra field failed!");
}
}
}
static void testTagOnlyHandling() throws Throwable {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] extra = new byte[] { 0x0a, 0, 4, 0, 0, 0, 0, 0 };
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
ZipEntry ze = new ZipEntry("TestExtraTime.java");
ze.setExtra(extra);
zos.putNextEntry(ze);
zos.write(new byte[] { 1,2 ,3, 4});
}
try (ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()))) {
ZipEntry ze = zis.getNextEntry();
check(ze, extra);
}
Path zpath = Paths.get(System.getProperty("test.dir", "."),
"TestExtraTime.zip");
Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
try (ZipFile zf = new ZipFile(zpath.toFile())) {
ZipEntry ze = zf.getEntry("TestExtraTime.java");
check(ze, extra);
} finally {
Files.delete(zpath);
}
}
static void checkLastModifiedTimeDOS(FileTime mtime, ZipEntry ze) {
FileTime lmt = ze.getLastModifiedTime();
if ((lmt.to(TimeUnit.SECONDS) >>> 1) != (mtime.to(TimeUnit.SECONDS) >>> 1) ||
lmt.to(TimeUnit.MILLISECONDS) != ze.getTime() ||
lmt.to(TimeUnit.MILLISECONDS) % 1000 != 0) {
throw new RuntimeException("Timestamp: storing mtime in dos format failed!");
}
}
static void testNullMtime() throws Throwable {
Instant now = Instant.now();
FileTime ctime = FileTime.from(now);
FileTime atime = FileTime.from(now.plusSeconds(7));
FileTime mtime = FileTime.from(now.plusSeconds(13));
System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n",
mtime, atime, ctime);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
ZipEntry ze = new ZipEntry("TestExtraTime.java");
ze.setCreationTime(ctime);
ze.setLastAccessTime(atime);
// ze.setLastModifiedTime(now);
ze.setTime(mtime.toMillis());
zos.putNextEntry(ze);
zos.write(new byte[] { 1,2 ,3, 4});
}
try (ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()))) {
ZipEntry ze = zis.getNextEntry();
// check LOC
check(null, atime, ctime, ze, null);
checkLastModifiedTimeDOS(mtime, ze);
}
Path zpath = Paths.get(System.getProperty("test.dir", "."),
"TestExtraTime.zip");
Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
try (ZipFile zf = new ZipFile(zpath.toFile())) {
ZipEntry ze = zf.getEntry("TestExtraTime.java");
// check CEN
checkLastModifiedTimeDOS(mtime, ze);
} finally {
Files.delete(zpath);
}
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8075526 8135108 8155616 8161942
* @summary Test timestamp via ZipEntry.get/setTimeLocal()
*/
import java.io.*;
import java.nio.file.*;
import java.time.*;
import java.util.*;
import java.util.zip.*;
public class TestLocalTime {
private static TimeZone tz0 = TimeZone.getDefault();
public static void main(String[] args) throws Throwable{
try {
LocalDateTime ldt = LocalDateTime.now();
test(ldt); // now
test(ldt.withYear(1968));
test(ldt.withYear(1970));
test(ldt.withYear(1982));
test(ldt.withYear(2037));
test(ldt.withYear(2100));
test(ldt.withYear(2106));
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
// dos time does not support < 1980, have to use
// utc in mtime.
testWithTZ(tz, ldt.withYear(1982));
testWithTZ(tz, ldt.withYear(2037));
testWithTZ(tz, ldt.withYear(2100));
testWithTZ(tz, ldt.withYear(2106));
// for #8135108
test(LocalDateTime.of(2100, 12, 06, 12, 34, 34, 973));
test(LocalDateTime.of(2106, 12, 06, 12, 34, 34, 973));
// for #8155616
test(LocalDateTime.of(2016, 03, 13, 2, 50, 00)); // gap
test(LocalDateTime.of(2015, 11, 1, 1, 30, 00)); // overlap
test(LocalDateTime.of(1968, 04, 28, 2, 51, 25));
test(LocalDateTime.of(1970, 04, 26, 2, 31, 52));
// for #8161942
test(LocalDateTime.of(2200, 04, 26, 2, 31, 52)); // unix 2038
} finally {
TimeZone.setDefault(tz0);
}
}
static byte[] getBytes(LocalDateTime mtime) throws Throwable {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry("TestLocalTime.java");
ze.setTimeLocal(mtime);
check(ze, mtime);
zos.putNextEntry(ze);
zos.write(new byte[] { 1, 2, 3, 4});
zos.close();
return baos.toByteArray();
}
static void testWithTZ(TimeZone tz, LocalDateTime ldt) throws Throwable {
TimeZone.setDefault(tz);
byte[] zbytes = getBytes(ldt);
TimeZone.setDefault(tz0);
test(zbytes, ldt);
}
static void test(LocalDateTime ldt) throws Throwable {
test(getBytes(ldt), ldt);
}
static void test(byte[] zbytes, LocalDateTime expected) throws Throwable {
System.out.printf("--------------------%nTesting: [%s]%n", expected);
// ZipInputStream
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zbytes));
ZipEntry ze = zis.getNextEntry();
zis.close();
check(ze, expected);
// ZipFile
Path zpath = Paths.get(System.getProperty("test.dir", "."),
"TestLocalTime.zip");
try {
Files.copy(new ByteArrayInputStream(zbytes), zpath);
ZipFile zf = new ZipFile(zpath.toFile());
ze = zf.getEntry("TestLocalTime.java");
check(ze, expected);
zf.close();
} finally {
Files.deleteIfExists(zpath);
}
}
static void check(ZipEntry ze, LocalDateTime expected) {
LocalDateTime ldt = ze.getTimeLocal();
if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
!= expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
// if the LDT is out of the range of the standard ms-dos date-time
// format ( < 1980 ) AND the date-time is within the daylight saving
// time gap (which means the LDT is actually "invalid"), the LDT will
// be adjusted accordingly when ZipEntry.setTimeLocal() converts the
// date-time via ldt -> zdt -> Instant -> FileTime.
// See ZonedDateTime.of(LocalDateTime, ZoneId) for more details.
if (ldt.getYear() < 1980 || ldt.getYear() > (1980 + 0x7f)) {
System.out.println(" Non-MSDOS ldt : " + ldt);
System.out.println(" expected : " + expected);
// try to adjust the "expected", assume daylight saving gap
expected = ZonedDateTime.of(expected, ZoneId.systemDefault())
.toLocalDateTime();
if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
== expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
return;
}
}
throw new RuntimeException("Timestamp: storing mtime failed!");
}
}
}

View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 7109837
* @summary Test Adler32/CRC32.update(ByteBuffer)
* @key randomness
*/
import java.util.*;
import java.util.zip.*;
import java.nio.*;
public class TimeChecksum {
static long time(Adler32 adler32, byte[] data, int iters, int len) {
long start_t = System.nanoTime();
for (int i = 0; i < iters; i++) {
adler32.reset();
adler32.update(data, 0, len);
}
long t = System.nanoTime() - start_t;
System.out.printf("%,12d", t / len);
return t;
}
static long time(Adler32 adler32, ByteBuffer buf, int iters) {
long start_t = System.nanoTime();
for (int i = 0; i < iters; i++) {
adler32.reset();
buf.mark();
adler32.update(buf);
buf.reset();
}
long t = System.nanoTime() - start_t;
System.out.printf("%,12d", t / buf.remaining());
return t;
}
static void testPosLimit(Adler32 adler32, ByteBuffer buf) {
int pos = buf.position();
int limit = buf.limit();
adler32.update(buf);
if (limit != buf.position() || limit != buf.limit()) {
System.out.printf("%nFAILED: pos,limit=(%d, %d), expected (%d, %d)%n",
buf.position(), buf.limit(), limit, limit);
throw new RuntimeException();
}
buf.position(pos);
}
static long time(CRC32 crc32, byte[] data, int iters, int len) {
long start_t = System.nanoTime();
for (int i = 0; i < iters; i++) {
crc32.reset();
crc32.update(data, 0, len);
}
long t = System.nanoTime() - start_t;
System.out.printf("%,12d", t / len);
return t;
}
static long time(CRC32 crc32, ByteBuffer buf, int iters) {
long start_t = System.nanoTime();
for (int i = 0; i < iters; i++) {
crc32.reset();
buf.mark();
crc32.update(buf);
buf.reset();
}
long t = System.nanoTime() - start_t;
System.out.printf("%,12d", t / buf.remaining());
return t;
}
static void testPosLimit(CRC32 crc32, ByteBuffer buf) {
int pos = buf.position();
int limit = buf.limit();
crc32.update(buf);
if (limit != buf.position() || limit != buf.limit()) {
System.out.printf("%nFAILED: pos,limit=(%d, %d), expected (%d, %d)%n",
buf.position(), buf.limit(), limit, limit);
throw new RuntimeException();
}
buf.position(pos);
}
public static void main(String[] args) {
int len = 1024 * 32;
int iters = 1;
if (args.length != 0 && "-benchmark".equals(args[0]))
iters = 100000;
Adler32 adler32 = new Adler32();
CRC32 crc32 = new CRC32();
Random rdm = new Random();
byte[] data = new byte[len];
new Random().nextBytes(data);
ByteBuffer buf;
System.out.println("---------- Adler32 ----------");
System.out.print("Warmup...");
time(adler32, data, iters, len);
time(adler32, ByteBuffer.wrap(data), iters);
buf = ByteBuffer.allocateDirect(len);
buf.put(data, 0, len);
buf.flip();
time(adler32, buf, iters);
System.out.println("\n");
System.out.println("Length byte[](ns/len) ByteBuffer(direct) ByteBuffer");
for (int testlen = 1; testlen < data.length; testlen <<= 1) {
System.out.print(testlen + "\t");
long baT = time(adler32, data, iters, testlen);
long baV = adler32.getValue();
System.out.print("\t");
buf = ByteBuffer.allocateDirect(testlen);
buf.put(data, 0, testlen);
buf.flip();
long bbdT = time(adler32, buf, iters);
long bbdV = adler32.getValue();
if (baV != bbdV) {
System.out.printf("%nFAILED: baV=%x,bbdV=%x%n", baV, bbdV);
throw new RuntimeException();
}
System.out.printf(" (%.2f)", (float)bbdT/baT);
testPosLimit(adler32, buf);
buf = ByteBuffer.allocate(testlen);
buf.put(data, 0, testlen);
buf.flip();
long bbT = time(adler32, buf, iters);
long bbV = adler32.getValue();
if (baV != bbV) {
System.out.printf("%nFAILED: baV=%x,bbV=%x%n", baV, bbV);
throw new RuntimeException();
}
testPosLimit(adler32, buf);
System.out.printf(" (%.2f) checksum=%x%n", (float)bbT/baT, bbV);
}
System.out.println("\n---------- CRC32 ----------");
System.out.print("Warmup...");
time(crc32, data, iters, len);
time(crc32, ByteBuffer.wrap(data), iters);
buf = ByteBuffer.allocateDirect(len);
buf.put(data, 0, len);
buf.flip();
time(crc32, buf, iters);
System.out.println("\n");
System.out.println("Length byte[](ns/len) ByteBuffer(direct) ByteBuffer");
for (int testlen = 1; testlen < data.length; testlen <<= 1) {
System.out.print(testlen + "\t");
long baT = time(crc32, data, iters, testlen);
long baV = crc32.getValue();
System.out.print("\t");
buf = ByteBuffer.allocateDirect(testlen);
buf.put(data, 0, testlen);
buf.flip();
long bbdT = time(crc32, buf, iters);
long bbdV = crc32.getValue();
if (baV != bbdV) {
System.out.printf("%nFAILED: baV=%x,bbdV=%x%n", baV, bbdV);
throw new RuntimeException();
}
System.out.printf(" (%.2f)", (float)bbdT/baT);
testPosLimit(crc32, buf);
buf = ByteBuffer.allocate(testlen);
buf.put(data, 0, testlen);
buf.flip();
long bbT = time(crc32, buf, iters);
long bbV = crc32.getValue();
if (baV != bbV) {
System.out.printf("%nFAILED: baV=%x,bbV=%x%n", baV, bbV);
throw new RuntimeException();
}
testPosLimit(crc32, buf);
System.out.printf(" (%.2f) checksum=%x%n", (float)bbT / baT, bbV);
}
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 7188852
* @summary Test De/Inflater.getBytesRead/Written()
* @key randomness
*/
import java.util.*;
import java.util.zip.*;
public class TotalInOut {
static final int BUF_SIZE= 1 * 1024 * 1024;
static void realMain (String[] args) throws Throwable {
long dataSize = 128L * 1024L * 1024L; // 128MB
if (args.length > 0 && "large".equals(args[0]))
dataSize = 5L * 1024L * 1024L * 1024L; // 5GB
try (final Deflater deflater = new Deflater();
final Inflater inflater = new Inflater()) {
byte[] dataIn = new byte[BUF_SIZE];
byte[] dataOut = new byte[BUF_SIZE];
byte[] tmp = new byte[BUF_SIZE];
Random r = new Random();
r.nextBytes(dataIn);
long bytesReadDef = 0;
long bytesWrittenDef = 0;
long bytesReadInf = 0;
long bytesWrittenInf = 0;
deflater.setInput(dataIn, 0, dataIn.length);
while (bytesReadDef < dataSize || bytesWrittenInf < dataSize) {
int len = r.nextInt(BUF_SIZE / 2) + BUF_SIZE / 2;
if (deflater.needsInput()) {
bytesReadDef += dataIn.length;
check(bytesReadDef == deflater.getBytesRead());
deflater.setInput(dataIn, 0, dataIn.length);
}
int n = deflater.deflate(tmp, 0, len);
bytesWrittenDef += n;
check(bytesWrittenDef == deflater.getBytesWritten());
inflater.setInput(tmp, 0, n);
bytesReadInf += n;
while (!inflater.needsInput()) {
bytesWrittenInf += inflater.inflate(dataOut, 0, dataOut.length);
check(bytesWrittenInf == inflater.getBytesWritten());
}
check(bytesReadInf == inflater.getBytesRead());
}
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void pass(String msg) {System.out.println(msg); passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void unexpected(Throwable t, String msg) {
System.out.println(msg); failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,207 @@
/*
* Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4244499 4532049 4700978 4820807 4980042 7009069 8322802
* @summary Test ZipInputStream, ZipOutputStream and ZipFile with non-UTF8 encoding
* @modules jdk.charsets
* @run junit ZipCoding
*/
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.*;
import java.nio.charset.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Stream;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.*;
public class ZipCoding {
// The data to write to ZIP entries in this test
private static byte[] ENTRY_DATA = "German Umlaut \u00fc in entry data"
.getBytes(StandardCharsets.ISO_8859_1);
/**
* Provide arguments used for parameterized tests
* @return a stream of argument lists
*/
public static Stream<Arguments> charsetsAndNames() {
// Arguments are: Write charset, read charset, entry name, comment
return Stream.of(
// MS code page 932 for the Japanese language
Arguments.of("MS932", "MS932",
"\u4e00\u4e01",
"\uff67\uff68\uff69\uff6a\uff6b\uff6c"),
// Code page for the IBM PC
Arguments.of("ibm437", "ibm437",
"\u00e4\u00fc",
"German Umlaut \u00fc in comment"),
// UTF-8 with Japanese characters
Arguments.of("utf-8", "utf-8",
"\u4e00\u4e01",
"\uff67\uff68\uff69\uff6a\uff6b\uff6c"),
// UTF-8 with characters in the Latin1 range
Arguments.of("utf-8", "utf-8",
"\u00e4\u00fc",
"German Umlaut \u00fc in comment"),
// UTF-8 with surrogate pairs
Arguments.of("utf-8", "utf-8",
"Surrogate\ud801\udc01",
"Surrogates \ud800\udc00 in comment"),
// ZipOutputStream sets the 'Language encoding flag' when writing using UTF-8
// UTF-8 should be used for decoding, regardless of the opening charset
// UTF-8 with Japanese characters, opened with MS932
Arguments.of("utf-8", "MS932",
"\u4e00\u4e01",
"\uff67\uff68\uff69\uff6a\uff6b\uff6c"),
// UTF-8 with characters in latin1 range, opened with iso-8859-1
Arguments.of("utf-8", "iso-8859-1",
"\u00e4\u00fc",
"German Umlaut \u00fc in comment"),
// UTF-8 with surrogate pairs, opened with MS932
Arguments.of("utf-8", "MS932",
"Surrogate\ud801\udc01",
"Surrogates \ud800\udc00 in comment")
);
}
/**
* Verify that ZipInputStream decodes entry names and comments
* using the charset provided to its constructor, or that it decodes
* using UTF-8 when the 'Language encoding flag' is set
*
* @param writeCharset the charset to use for ZipOutputStream when producing the ZIP
* @param readCharset the charset to use when opening the ZipInputStream
* @param name the entry name
* @param comment the entry comment (not read by ZipInputStream)
*
* @throws IOException if an unexpected IOException occurs
*/
@ParameterizedTest
@MethodSource("charsetsAndNames")
public void testZipInputStream(String writeCharset,
String readCharset,
String name,
String comment) throws IOException {
byte[] zip = createZIP(writeCharset, name, comment);
try (InputStream in = new ByteArrayInputStream(zip);
ZipInputStream zis = new ZipInputStream(in, Charset.forName(readCharset))) {
ZipEntry e = zis.getNextEntry();
assertNotNull(e);
assertEquals(name, e.getName(),
"ZipInputStream.getNextEntry() returned unexpected entry name");
assertNull(e.getComment()); // No comment in the LOC header
assertArrayEquals(ENTRY_DATA, zis.readAllBytes(), "Unexpected ZIP entry data");
}
}
/**
* Verify that ZipFile decodes entry names and comments
* using the charset provided to its constructor, or that it decodes
* using UTF-8 when the 'Language encoding flag' is set
*
* @param writeCharset the charset to use for ZipOutputStream when producing the ZIP
* @param readCharset the charset to use when opening the ZipFile
* @param name the name of the entry
* @param comment the comment of the entry
*
* @throws IOException if an unexpected IOException occurs
*/
@ParameterizedTest
@MethodSource("charsetsAndNames")
public void testZipFile(String writeCharset,
String readCharset,
String name,
String comment) throws IOException {
byte[] zip = createZIP(writeCharset, name, comment);
Path f = Path.of("zfcoding.zip");
Files.write(f, zip);
try (ZipFile zf = new ZipFile(f.toFile(), Charset.forName(readCharset))) {
// Test using ZipFile.entries
Enumeration<? extends ZipEntry> zes = zf.entries();
ZipEntry e = (ZipEntry)zes.nextElement();
assertNotNull(e);
assertEquals(name, e.getName(), "ZipFile.entries() returned unexpected entry name");
assertEquals(comment, e.getComment(), "ZipFile.entries() returned unexpected entry comment");
// Test using ZipFile.getEntry
e = zf.getEntry(name);
assertNotNull(e,
String.format("Entry lookup failed on ZIP encoded with %s and opened with %s",
writeCharset, readCharset));
assertEquals(name, e.getName(), "ZipFile.getEntry() returned unexpected entry name");
assertEquals(comment, e.getComment(), "ZipFile.getEntry() returned unexpected entry comment");
try (InputStream is = zf.getInputStream(e)) {
assertNotNull(is);
assertArrayEquals(ENTRY_DATA, is.readAllBytes(), "Unexpected ZIP entry data");
}
}
Files.deleteIfExists(f);
}
/**
* Create a ZIP file containing an entry with the given name
* and comment, encoded using the given charset.
* Note that if the charset is UTF-8, ZipOutputStream will
* set the 'Language encoding flag' for the entry.
*
* @param charset the charset passed to the ZipOutputStream constructor
* @param name the name of the entry to add
* @param comment the comment of the entry to add
* @return a byte array containing the ZIP file
*
* @throws IOException if an unexpected IOException occurs
*/
private byte[] createZIP(String charset, String name, String comment) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos, Charset.forName(charset))) {
ZipEntry e = new ZipEntry(name);
e.setComment(comment);
zos.putNextEntry(e);
zos.write(ENTRY_DATA);
zos.closeEntry();
}
return baos.toByteArray();
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4134526
@summary Test if ZipEntry.clone() works when the extra field is null.
*/
import java.util.zip.*;
import java.io.*;
public class Clone {
public static void main(String argv[]) throws Exception {
ZipFile zf = new ZipFile(new File(System.getProperty("test.src"),
"input.jar"));
ZipEntry e = new ZipEntry("foo");
e.clone();
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4243169
@summary Make sure ZipEntry static initializer will load the
zip library.
*/
import java.util.zip.ZipEntry;
import java.util.jar.JarEntry;
import java.io.*;
public class Constructor {
public static void main(String argv[]) throws Exception {
ZipEntry e1 = new ZipEntry("foo");
JarEntry e2 = new JarEntry("bar");
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8340553
* @summary Verify that ZipEntry(String), ZipEntry::setComment, and
* ZipEntry::setExtra throws a IllegalArgumentException when the
* combined length of the fields, including the size of the CEN Header,
* exceeds 65,535 bytes
* @run junit MaxZipEntryFieldSizeTest
*/
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class MaxZipEntryFieldSizeTest {
// CEN header size + name length + comment length + extra length
// should not exceed 65,535 bytes per the PKWare APP.NOTE
// 4.4.10, 4.4.11, & 4.4.12.
static final int MAX_COMBINED_CEN_HEADER_SIZE = 0xFFFF;
// Maximum possible size of name length + comment length + extra length
// for entries in order to not exceed 65,489 bytes
static final int MAX_NAME_COMMENT_EXTRA_SIZE =
MAX_COMBINED_CEN_HEADER_SIZE - ZipFile.CENHDR;
// Tag for the 'unknown' field type, specified in APPNOTE.txt 'Third party mappings'
static final short UNKNOWN_ZIP_TAG = (short) 0x9902;
// Zip Entry name used by tests
static final String ENTRY_NAME = "EntryName";
// Max length minus the size of the ENTRY_NAME or ENTRY_COMMENT
static final int MAX_FIELD_LEN_MINUS_ENTRY_NAME =
MAX_NAME_COMMENT_EXTRA_SIZE - 9;
/**
* Validate an IllegalArgumentException is thrown when the
* combined length of the entry name, entry comment, entry extra data,
* and CEN Header size exceeds 65,535 bytes.
*/
@ParameterizedTest
@ValueSource(ints = {30000, 35000})
void combinedLengthTest(int length) {
String comment = "a".repeat(length);
byte[] bytes = creatExtraData(length);
int combinedLength = ENTRY_NAME.length() + comment.length() + bytes.length;
boolean expectException = combinedLength > MAX_COMBINED_CEN_HEADER_SIZE;
System.out.printf("Combined Len= %s, exception: %s%n", combinedLength, expectException);
ZipEntry zipEntry = new ZipEntry(ENTRY_NAME);
zipEntry.setComment(comment);
// The extra data length will trigger the IllegalArgumentException
if (expectException) {
assertThrows(IllegalArgumentException.class, () ->
zipEntry.setExtra(bytes));
} else {
zipEntry.setExtra(bytes);
}
}
/**
* Validate an IllegalArgumentException is thrown when the comment
* length exceeds 65,489 bytes.
*/
@ParameterizedTest
@ValueSource(ints = {MAX_COMBINED_CEN_HEADER_SIZE,
MAX_NAME_COMMENT_EXTRA_SIZE,
MAX_NAME_COMMENT_EXTRA_SIZE + 1,
MAX_FIELD_LEN_MINUS_ENTRY_NAME,
MAX_FIELD_LEN_MINUS_ENTRY_NAME - 1})
void setCommentLengthTest(int length) {
boolean expectException = length >= MAX_NAME_COMMENT_EXTRA_SIZE;
ZipEntry zipEntry = new ZipEntry(ENTRY_NAME);
String comment = "a".repeat(length);
System.out.printf("Comment Len= %s, exception: %s%n", comment.length(), expectException);
// The comment length will trigger the IllegalArgumentException
if (expectException) {
assertThrows(IllegalArgumentException.class, () ->
zipEntry.setComment(comment));
} else {
zipEntry.setComment(comment);
}
}
/**
* Validate an IllegalArgumentException is thrown when the name
* length exceeds 65,489 bytes.
*/
@ParameterizedTest
@ValueSource(ints = {MAX_COMBINED_CEN_HEADER_SIZE,
MAX_NAME_COMMENT_EXTRA_SIZE,
MAX_NAME_COMMENT_EXTRA_SIZE + 1,
MAX_FIELD_LEN_MINUS_ENTRY_NAME,
MAX_FIELD_LEN_MINUS_ENTRY_NAME - 1})
void nameLengthTest(int length) {
boolean expectException = length > MAX_NAME_COMMENT_EXTRA_SIZE;
String name = "a".repeat(length);
System.out.printf("name Len= %s, exception: %s%n", name.length(), expectException);
// The name length will trigger the IllegalArgumentException
if (expectException) {
assertThrows(IllegalArgumentException.class, () -> new ZipEntry(name));
} else {
new ZipEntry(name);
}
}
/**
* Validate an IllegalArgumentException is thrown when the extra data
* length exceeds 65,489 bytes.
*/
@ParameterizedTest
@ValueSource(ints = {MAX_COMBINED_CEN_HEADER_SIZE,
MAX_NAME_COMMENT_EXTRA_SIZE,
MAX_NAME_COMMENT_EXTRA_SIZE + 1,
MAX_FIELD_LEN_MINUS_ENTRY_NAME,
MAX_FIELD_LEN_MINUS_ENTRY_NAME - 1})
void setExtraLengthTest(int length) {
boolean expectException = length >= MAX_NAME_COMMENT_EXTRA_SIZE;
byte[] bytes = creatExtraData(length);
ZipEntry zipEntry = new ZipEntry(ENTRY_NAME);
System.out.printf("extra Len= %s, exception: %s%n", bytes.length, expectException);
// The extra data length will trigger the IllegalArgumentException
if (expectException) {
assertThrows(IllegalArgumentException.class, () -> zipEntry.setExtra(bytes));
} else {
zipEntry.setExtra(bytes);
}
}
/**
* Create the extra field data which will be passed to ZipEntry::setExtra
* @param length size of the extra data
* @return byte array containing the extra data
*/
private static byte[] creatExtraData(int length) {
byte[] bytes = new byte[length];
// Little-endian ByteBuffer for updating the header fields
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
// We use the 'unknown' tag, specified in APPNOTE.TXT, 4.6.1 Third party mappings'
buffer.putShort(UNKNOWN_ZIP_TAG);
// Size of the actual (empty) data
buffer.putShort((short) (length - 2 * Short.BYTES));
return bytes;
}
}

Binary file not shown.

View file

@ -0,0 +1,331 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4770745 6234507 6303183 8048990
* @summary test a variety of zip file entries
* @author Martin Buchholz
* @key randomness
*/
import java.util.*;
import java.util.zip.*;
import java.util.jar.*;
import java.io.*;
public class Assortment {
static int passed = 0, failed = 0;
static void fail(String msg) {
failed++;
new Exception(msg).printStackTrace();
}
static void unexpected(Throwable t) {
failed++;
t.printStackTrace();
}
static void check(boolean condition, String msg) {
if (! condition)
fail(msg);
}
static void check(boolean condition) {
check(condition, "Something's wrong");
}
static final int get16(byte b[], int off) {
return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8);
}
// check if all "expected" extra fields equal to their
// corresponding fields in "extra". The "extra" might have
// timestamp fields added by ZOS.
static boolean equalsExtraData(byte[] expected, byte[] extra) {
if (expected == null)
return true;
int off = 0;
int len = expected.length;
while (off + 4 < len) {
int tag = get16(expected, off);
int sz = get16(expected, off + 2);
int off0 = 0;
int len0 = extra.length;
boolean matched = false;
while (off0 + 4 < len0) {
int tag0 = get16(extra, off0);
int sz0 = get16(extra, off0 + 2);
if (tag == tag0 && sz == sz0) {
matched = true;
for (int i = 0; i < sz; i++) {
if (expected[off + i] != extra[off0 +i])
matched = false;
}
break;
}
off0 += (4 + sz0);
}
if (!matched)
return false;
off += (4 + sz);
}
return true;
}
private static class Entry {
private String name;
private int method;
private byte[] data;
private byte[] extra;
private String comment;
Entry(String name,
int method,
byte[] data,
byte[] extra,
String comment) {
this.name = name;
this.method = method;
this.data = data;
this.extra = extra;
this.comment = comment;
}
void write(ZipOutputStream s) throws Exception {
ZipEntry e = new ZipEntry(name);
CRC32 crc32 = new CRC32();
e.setMethod(method);
if (method == ZipEntry.STORED) {
e.setSize(data == null ? 0 : data.length);
crc32.reset();
if (data != null) crc32.update(data);
e.setCrc(crc32.getValue());
} else {
e.setSize(0);
e.setCrc(0);
}
if (comment != null) e.setComment(comment);
if (extra != null) e.setExtra(extra);
s.putNextEntry(e);
if (data != null) s.write(data);
}
byte[] getData(ZipFile f, ZipEntry e) throws Exception {
byte[] fdata = new byte[(int)e.getSize()];
InputStream is = f.getInputStream(e);
is.read(fdata);
return fdata;
}
void verify(ZipFile f, ZipEntry e) throws Exception {
verify(e, getData(f, e));
}
void verify(ZipEntry e, byte[] eData) throws Exception {
byte[] data = (this.data == null) ? new byte[]{} : this.data;
byte[] extra = (this.extra != null && this.extra.length == 0) ?
null : this.extra;
check(name.equals(e.getName()));
check(method == e.getMethod());
check((((comment == null) || comment.equals(""))
&& (e.getComment() == null))
|| comment.equals(e.getComment()));
check(equalsExtraData(extra, e.getExtra()));
check(Arrays.equals(data, eData));
check(e.getSize() == data.length);
check((method == ZipEntry.DEFLATED) ||
(e.getCompressedSize() == data.length));
}
void verify(ZipFile f) throws Exception {
ZipEntry e = f.getEntry(name);
verify(e, getData(f, e));
}
void verifyZipInputStream(ZipInputStream s) throws Exception {
ZipEntry e = s.getNextEntry();
byte[] data = (this.data == null) ? new byte[]{} : this.data;
byte[] otherData = new byte[data.length];
s.read(otherData);
check(Arrays.equals(data, otherData));
byte[] extra = (this.extra != null && this.extra.length == 0) ?
null : this.extra;
check(equalsExtraData(extra, e.getExtra()));
check(name.equals(e.getName()));
check(method == e.getMethod());
check(e.getSize() == -1 || e.getSize() == data.length);
check((method == ZipEntry.DEFLATED) ||
(e.getCompressedSize() == data.length));
}
void verifyJarInputStream(JarInputStream s) throws Exception {
// JarInputStream "automatically" reads the manifest
if (name.equals("meta-iNf/ManIfEst.Mf"))
return;
verifyZipInputStream(s);
}
}
private static int uniquifier = 86;
private static String uniquify(String name) {
return name + (uniquifier++);
}
private static byte[] toBytes(String s) throws Exception {
return s.getBytes("UTF-8");
}
private static byte[] toExtra(byte[] bytes) throws Exception {
if (bytes == null) return null;
// Construct a fake extra field with valid header length
byte[] v = new byte[bytes.length + 4];
v[0] = (byte) 0x47;
v[1] = (byte) 0xff;
v[2] = (byte) bytes.length;
v[3] = (byte) (bytes.length << 8);
System.arraycopy(bytes, 0, v, 4, bytes.length);
return v;
}
private static Random random = new Random();
private static String makeName(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
sb.append((char)(random.nextInt(10000)+1));
return sb.toString();
}
public static void main(String[] args) throws Exception {
File zipName = new File("x.zip");
int[] methods = {ZipEntry.STORED, ZipEntry.DEFLATED};
String[] names = {makeName(1), makeName(160), makeName(9000)};
byte[][] datas = {null, new byte[]{}, new byte[]{'d'}};
byte[][] extras = {null, new byte[]{}, new byte[]{'e'}};
String[] comments = {null, "", "c"};
List<Entry> entries = new ArrayList<Entry>();
// Highly unusual manifest
entries.add(new Entry("meta-iNf/ManIfEst.Mf",
ZipEntry.STORED,
toBytes("maNiFest-VeRsIon: 1.0\n"),
toExtra(toBytes("Can manifests have extra??")),
"Can manifests have comments??"));
// The emptiest possible entry
entries.add(new Entry("", ZipEntry.STORED, null, null, ""));
for (String name : names)
for (int method : methods)
for (byte[] data : datas) // datae??
for (byte[] extra : extras)
for (String comment : comments)
entries.add(new Entry(uniquify(name), method, data,
toExtra(extra), comment));
//----------------------------------------------------------------
// Write zip file using ZipOutputStream
//----------------------------------------------------------------
try (FileOutputStream fos = new FileOutputStream(zipName);
ZipOutputStream zos = new ZipOutputStream(fos))
{
for (Entry e : entries)
e.write(zos);
}
//----------------------------------------------------------------
// Verify zip file contents using ZipFile.getEntry()
//----------------------------------------------------------------
try (ZipFile f = new ZipFile(zipName)) {
for (Entry e : entries)
e.verify(f);
}
//----------------------------------------------------------------
// Verify zip file contents using JarFile.getEntry()
//----------------------------------------------------------------
try (JarFile f = new JarFile(zipName)) {
check(f.getManifest() != null);
for (Entry e : entries)
e.verify(f);
}
//----------------------------------------------------------------
// Verify zip file contents using ZipFile.entries()
//----------------------------------------------------------------
try (ZipFile f = new ZipFile(zipName)) {
Enumeration<? extends ZipEntry> en = f.entries();
for (Entry e : entries)
e.verify(f, en.nextElement());
check(!en.hasMoreElements());
}
//----------------------------------------------------------------
// Verify zip file contents using JarFile.entries()
//----------------------------------------------------------------
try (JarFile f = new JarFile(zipName)) {
Enumeration<? extends ZipEntry> en = f.entries();
for (Entry e : entries)
e.verify(f, en.nextElement());
check(!en.hasMoreElements());
}
//----------------------------------------------------------------
// Verify zip file contents using ZipInputStream class
//----------------------------------------------------------------
try (FileInputStream fis = new FileInputStream(zipName);
ZipInputStream s = new ZipInputStream(fis)) {
for (Entry e : entries)
e.verifyZipInputStream(s);
}
//----------------------------------------------------------------
// Verify zip file contents using JarInputStream class
//----------------------------------------------------------------
try (FileInputStream fis = new FileInputStream(zipName);
JarInputStream s = new JarInputStream(fis)) {
// JarInputStream "automatically" reads the manifest
check(s.getManifest() != null);
for (Entry e : entries)
e.verifyJarInputStream(s);
}
// String cmd = "unzip -t " + zipName.getPath() + " >/dev/tty";
// new ProcessBuilder(new String[]{"/bin/sh", "-c", cmd}).start().waitFor();
zipName.deleteOnExit();
System.out.printf("passed = %d, failed = %d%n", passed, failed);
if (failed > 0) throw new Exception("Some tests failed");
}
}

View file

@ -0,0 +1,246 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8341595
* @modules java.base/jdk.internal.util
* @summary Verify that ZipFile can read from a ZIP file with a maximally large CEN size
* @run junit/othervm/manual -Xmx2500M CenSizeMaximum
*/
import jdk.internal.util.ArraysSupport;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.*;
public class CenSizeMaximum {
// Maximum allowed CEN size allowed by the ZipFile implementation
static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH;
/**
* From the APPNOTE.txt specification:
* 4.4.10 file name length: (2 bytes)
* 4.4.11 extra field length: (2 bytes)
* 4.4.12 file comment length: (2 bytes)
*
* The length of the file name, extra field, and comment
* fields respectively. The combined length of any
* directory record and these three fields SHOULD NOT
* generally exceed 65,535 bytes.
*.
* Create a maximum extra field which does not exceed 65,535 bytes
*/
static final int MAX_EXTRA_FIELD_SIZE = 65_535 - ZipFile.CENHDR;
// Tag for the 'unknown' field type, specified in APPNOTE.txt 'Third party mappings'
static final short UNKNOWN_ZIP_TAG = (short) 0x9902;
// The size of one CEN header, including the name and the extra field
static final int CEN_HEADER_SIZE = ZipFile.CENHDR + MAX_EXTRA_FIELD_SIZE;
// The size of the extra data field header (tag id + data block length)
static final int EXTRA_FIELD_HEADER_SIZE = 2 * Short.BYTES;
// Zip file to create for testing
private Path hugeZipFile = Path.of("cen-size-on-limit.zip");
/**
* Clean up ZIP file created in this test
*/
@AfterEach
public void cleanup() throws IOException {
//Files.deleteIfExists(hugeZipFile);
}
/**
* Validates that ZipFile opens a ZIP file with a CEN size close
* to the {@link #MAX_CEN_SIZE} implementation limit.
*
* @throws IOException if an unexpected IO error occurs
*/
@Test
public void maximumCenSize() throws IOException {
int numCenHeaders = zipWithWithExactCenSize(MAX_CEN_SIZE, true, false);
try (var zf = new ZipFile(hugeZipFile.toFile())) {
assertEquals(numCenHeaders, zf.size());
}
}
/**
* Validates that ZipFile rejects a ZIP where the last CEN record
* overflows the CEN size and the END header CENTOT field is smaller
* than the actual number of headers
*
* @throws IOException if an unexpected IO error occurs
*/
@Test
public void lastCENHeaderBadSize() throws IOException {
int numCenHeaders = zipWithWithExactCenSize(1024, true, true);
ZipException zipException = assertThrows(ZipException.class, () -> {
try (var zf = new ZipFile(hugeZipFile.toFile())) {
assertEquals(numCenHeaders, zf.size());
}
});
assertEquals("invalid CEN header (bad header size)", zipException.getMessage());
}
/**
* Produce a ZIP file with an exact CEN size. To minimize the number of CEN headers
* written, maximally large, empty extra data blocks are written sparsely.
*
* @param cenSize the exact CEN size of the ZIP file to produce
* @param invalidateEndTotal whether to decrement the END header's TOT field by one
* @return the number of CEN headers produced
* @throws IOException if an unexpected IO error occurs
*/
private int zipWithWithExactCenSize(long cenSize, boolean invalidateEndTotal, boolean overflowLastCEN)
throws IOException {
// Sanity check
assertTrue(cenSize <= MAX_CEN_SIZE);
// The number of CEN headers we need to write
int numCenHeaders = (int) (cenSize / CEN_HEADER_SIZE) + 1;
// Size if all extra data fields were of maximum size
long overSized = numCenHeaders * (long) CEN_HEADER_SIZE;
// Length to trim from the first CEN's extra data
int negativPadding = (int) (overSized - cenSize);
int firstExtraSize = MAX_EXTRA_FIELD_SIZE - negativPadding;
// Sanity check
long computedCenSize = (numCenHeaders -1L ) * CEN_HEADER_SIZE + ZipEntry.CENHDR + firstExtraSize;
assertEquals(computedCenSize, cenSize);
// A CEN header, followed by the four-bytes extra data header
ByteBuffer cenHeader = createCENHeader();
// An END header
ByteBuffer endHeader = createENDHeader();
// Update the END header
if (invalidateEndTotal) {
// To trigger countCENHeaders
endHeader.putShort(ZipEntry.ENDTOT, (short) (numCenHeaders -1 & 0xFFFF));
} else {
endHeader.putShort(ZipEntry.ENDTOT, (short) (numCenHeaders & 0xFFFF));
}
// Update CEN size and offset fields
endHeader.putInt(ZipEntry.ENDSIZ, (int) (cenSize & 0xFFFFFFFFL));
endHeader.putInt(ZipEntry.ENDOFF, 0);
// When creating a sparse file, the file must not already exit
Files.deleteIfExists(hugeZipFile);
// Open a FileChannel for writing a sparse file
EnumSet<StandardOpenOption> options = EnumSet.of(StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE,
StandardOpenOption.SPARSE);
try (FileChannel channel = FileChannel.open(hugeZipFile, options)) {
// Write CEN headers
for (int i = 0; i < numCenHeaders; i++) {
// The first CEN header has trimmed extra data
int extraSize = i == 0 ? firstExtraSize : MAX_EXTRA_FIELD_SIZE;
if (overflowLastCEN && i == numCenHeaders - 1) {
// make last CEN header overflow the CEN size
cenHeader.putShort(ZipEntry.CENNAM, Short.MAX_VALUE);
}
// update elen field
cenHeader.putShort(ZipEntry.CENEXT, (short) (extraSize & 0xFFFF));
// update data block len of the extra field header
short dlen = (short) ((extraSize - EXTRA_FIELD_HEADER_SIZE) & 0xFFFF);
cenHeader.putShort(ZipEntry.CENHDR + Short.BYTES, dlen);
// Write the CEN header plus the four-byte extra header
channel.write(cenHeader.rewind());
// Sparse "write" of the extra data block
channel.position(channel.position() + extraSize - EXTRA_FIELD_HEADER_SIZE);
}
// Sanity check
assertEquals(cenSize, channel.position());
// Write the END header
channel.write(endHeader.rewind());
}
return numCenHeaders;
}
// Creates a ByteBuffer representing a CEN header with a trailing extra field header
private ByteBuffer createCENHeader() throws IOException {
byte[] bytes = smallZipfile();
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
int endOff = bytes.length - ZipEntry.ENDHDR;
int cenSize = buf.getInt(endOff + ZipEntry.ENDSIZ);
int cenOff = buf.getInt(endOff + ZipEntry.ENDOFF);
return ByteBuffer.wrap(
Arrays.copyOfRange(bytes, cenOff, cenOff + ZipEntry.CENHDR + EXTRA_FIELD_HEADER_SIZE)
).order(ByteOrder.LITTLE_ENDIAN);
}
// Creates a ByteBuffer representing an END header
private ByteBuffer createENDHeader() throws IOException {
byte[] bytes = smallZipfile();
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
int endOff = bytes.length - ZipEntry.ENDHDR;
return ByteBuffer.wrap(
Arrays.copyOfRange(bytes, endOff, endOff + ZipEntry.ENDHDR)
).order(ByteOrder.LITTLE_ENDIAN);
}
// Create a byte array with a minimal ZIP file
private static byte[] smallZipfile() throws IOException {
var out = new ByteArrayOutputStream();
try (var zo = new ZipOutputStream(out)) {
ZipEntry entry = new ZipEntry("");
entry.setExtra(makeDummyExtraField());
zo.putNextEntry(entry);
}
return out.toByteArray();
}
// Create a minimally sized extra field
private static byte[] makeDummyExtraField() {
byte[] extra = new byte[EXTRA_FIELD_HEADER_SIZE];
// Little-endian ByteBuffer for updating the header fields
ByteBuffer buffer = ByteBuffer.wrap(extra).order(ByteOrder.LITTLE_ENDIAN);
// We use the 'unknown' tag, specified in APPNOTE.TXT, 4.6.1 Third party mappings'
buffer.putShort(UNKNOWN_ZIP_TAG);
// Size of the actual (empty) data
buffer.putShort((short) 0);
return extra;
}
}

View file

@ -0,0 +1,221 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8272746
* @modules java.base/jdk.internal.util
* @summary Verify that ZipFile rejects a ZIP with a CEN size which does not fit in a Java byte array
* @run junit CenSizeTooLarge
*/
import jdk.internal.util.ArraysSupport;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.time.LocalDateTime;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class CenSizeTooLarge {
// Entry names produced in this test are fixed-length
public static final int NAME_LENGTH = 10;
// Maximum allowed CEN size allowed by the ZipFile implementation
static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH;
/**
* From the APPNOTE.txt specification:
* 4.4.10 file name length: (2 bytes)
* 4.4.11 extra field length: (2 bytes)
* 4.4.12 file comment length: (2 bytes)
*
* The length of the file name, extra field, and comment
* fields respectively. The combined length of any
* directory record and these three fields SHOULD NOT
* generally exceed 65,535 bytes.
*.
* Create a maximum extra field which does not exceed 65,535 bytes
*/
static final int MAX_EXTRA_FIELD_SIZE = 65_535 - ZipFile.CENHDR - NAME_LENGTH;
// Data size (unsigned short)
// Field size minus the leading header 'tag' and 'data size' fields (2 bytes each)
static final short MAX_DATA_SIZE = (short) (MAX_EXTRA_FIELD_SIZE - 2 * Short.BYTES);
// Tag for the 'unknown' field type, specified in APPNOTE.txt 'Third party mappings'
static final short UNKNOWN_ZIP_TAG = (short) 0x9902;
// Use a shared LocalDateTime on all entries to save processing time
static final LocalDateTime TIME_LOCAL = LocalDateTime.now();
// The size of one CEN header, including the name and the extra field
static final int CEN_HEADER_SIZE = ZipFile.CENHDR + NAME_LENGTH + MAX_EXTRA_FIELD_SIZE;
// The number of entries needed to exceed the MAX_CEN_SIZE
static final int NUM_ENTRIES = (MAX_CEN_SIZE / CEN_HEADER_SIZE) + 1;
// Expected ZipException message when the CEN does not fit in a Java byte array
private static final String CEN_TOO_LARGE_MESSAGE = "invalid END header (central directory size too large)";
// Zip file to create for testing
private File hugeZipFile;
private static final byte[] EXTRA_BYTES = makeLargeExtraField();
// Helps SparseOutputStream detect write of the last CEN entry
private static final byte[] LAST_EXTRA_BYTES = makeLargeExtraField();
/**
* Create a zip file with a CEN size which does not fit within a Java byte array
*/
@Before
public void setup() throws IOException {
hugeZipFile = new File("cen-too-large.zip");
hugeZipFile.deleteOnExit();
try (OutputStream out = new SparseOutputStream(new FileOutputStream(hugeZipFile));
ZipOutputStream zip = new ZipOutputStream(out)) {
// Keep track of entries so we can update extra data before the CEN is written
ZipEntry[] entries = new ZipEntry[NUM_ENTRIES];
// Add entries until MAX_CEN_SIZE is reached
for (int i = 0; i < NUM_ENTRIES; i++) {
// Create a fixed-length name for the entry
String name = Integer.toString(i);
name = "0".repeat(NAME_LENGTH - name.length()) + name;
// Create and track the entry
ZipEntry entry = entries[i] = new ZipEntry(name);
// Use STORED for faster processing
entry.setMethod(ZipEntry.STORED);
entry.setSize(0);
entry.setCrc(0);
// Set the time/date field for faster processing
entry.setTimeLocal(TIME_LOCAL);
// Add the entry
zip.putNextEntry(entry);
}
// Finish writing the last entry
zip.closeEntry();
// Before the CEN headers are written, set the extra data on each entry
for (ZipEntry entry : entries) {
entry.setExtra(EXTRA_BYTES);
}
// Help SparseOutputSream detect the last entry
entries[entries.length-1].setExtra(LAST_EXTRA_BYTES);
}
}
/**
* Validates that a ZipException is thrown with the expected message when
* the ZipFile is initialized with a ZIP whose CEN exeeds {@link #MAX_CEN_SIZE}
*/
@Test
public void centralDirectoryTooLargeToFitInByteArray() {
ZipException ex = assertThrows(ZipException.class, () -> new ZipFile(hugeZipFile));
assertEquals(CEN_TOO_LARGE_MESSAGE, ex.getMessage());
}
/**
* We can reduce the number of written CEN headers by making each CEN header maximally large.
* We do this by adding the extra field produced by this method to each CEN header.
* <p>
* The structure of an extra field is as follows:
* <p>
* Header ID (Two bytes, describes the type of the field, also called 'tag')
* Data Size (Two byte short)
* Data Block (Contents depend on field type)
*/
private static byte[] makeLargeExtraField() {
// Make a maximally sized extra field
byte[] extra = new byte[MAX_EXTRA_FIELD_SIZE];
// Little-endian ByteBuffer for updating the header fields
ByteBuffer buffer = ByteBuffer.wrap(extra).order(ByteOrder.LITTLE_ENDIAN);
// We use the 'unknown' tag, specified in APPNOTE.TXT, 4.6.1 Third party mappings'
buffer.putShort(UNKNOWN_ZIP_TAG);
// Size of the actual (empty) data
buffer.putShort(MAX_DATA_SIZE);
return extra;
}
/**
* By writing sparse 'holes' until the last CEN is detected, we can save disk space
* used by this test from ~2GB to ~4K. Instances of this class should be passed
* directly to the ZipOutputStream constructor, without any buffering. Otherwise,
* writes from ZipOutputStream may not be detected correctly.
*/
private static class SparseOutputStream extends FilterOutputStream {
private final FileChannel channel;
private boolean sparse = true; // True until the last CEN is written
private long position = 0;
public SparseOutputStream(FileOutputStream fos) {
super(fos);
this.channel = fos.getChannel();
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
position += len;
if (sparse) {
// Until finding the last CEN, we don't actually write anything,
// but instead simply advance the position, creating a sparse file
channel.position(position);
// Check for last CEN record
if (b == LAST_EXTRA_BYTES) {
// From here on, write actual bytes
sparse = false;
}
} else {
// Regular write
out.write(b, off, len);
}
}
@Override
public void write(int b) throws IOException {
position++;
if (sparse) {
channel.position(position);
} else {
out.write(b);
}
}
}
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Portions Copyright (c) 2011 IBM Corporation
*/
/*
* @test
* @bug 7031076
* @summary Allow stale InputStreams from ZipFiles to be GC'd
* @author Neil Richards <neil.richards@ngmr.net>, <neil_richards@uk.ibm.com>
* @key randomness
*/
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ClearStaleZipFileInputStreams {
private static final int ZIP_ENTRY_NUM = 5;
private static final byte[][] data;
static {
data = new byte[ZIP_ENTRY_NUM][];
Random r = new Random();
for (int i = 0; i < ZIP_ENTRY_NUM; i++) {
data[i] = new byte[1000];
r.nextBytes(data[i]);
}
}
private static File createTestFile(int compression) throws Exception {
File tempZipFile =
File.createTempFile("test-data" + compression, ".zip");
tempZipFile.deleteOnExit();
try (FileOutputStream fos = new FileOutputStream(tempZipFile);
ZipOutputStream zos = new ZipOutputStream(fos)) {
zos.setLevel(compression);
for (int i = 0; i < ZIP_ENTRY_NUM; i++) {
String text = "Entry" + i;
ZipEntry entry = new ZipEntry(text);
zos.putNextEntry(entry);
try {
zos.write(data[i], 0, data[i].length);
} finally {
zos.closeEntry();
}
}
}
return tempZipFile;
}
private static final class GcInducingThread extends Thread {
private final int sleepMillis;
private boolean keepRunning = true;
public GcInducingThread(final int sleepMillis) {
this.sleepMillis = sleepMillis;
}
public synchronized void run() {
while (keepRunning) {
System.gc();
try {
wait(sleepMillis);
} catch (InterruptedException e) {
System.out.println("GCing thread unexpectedly interrupted");
return;
}
}
}
public synchronized void shutDown() {
keepRunning = false;
notifyAll();
}
}
public static void main(String[] args) throws Exception {
GcInducingThread gcThread = new GcInducingThread(500);
gcThread.start();
try {
runTest(ZipOutputStream.DEFLATED);
runTest(ZipOutputStream.STORED);
} finally {
gcThread.shutDown();
gcThread.join();
}
}
private static void runTest(int compression) throws Exception {
ReferenceQueue<InputStream> rq = new ReferenceQueue<>();
System.out.println("Testing with a zip file with compression level = "
+ compression);
File f = createTestFile(compression);
try (ZipFile zf = new ZipFile(f)) {
Set<Object> refSet = createTransientInputStreams(zf, rq);
System.out.println("Waiting for 'stale' input streams from ZipFile to be GC'd ...");
System.out.println("(The test will hang on failure)");
while (false == refSet.isEmpty()) {
refSet.remove(rq.remove());
}
System.out.println("Test PASSED.");
System.out.println();
} finally {
f.delete();
}
}
private static Set<Object> createTransientInputStreams(ZipFile zf,
ReferenceQueue<InputStream> rq) throws Exception {
Enumeration<? extends ZipEntry> zfe = zf.entries();
Set<Object> refSet = new HashSet<>();
while (zfe.hasMoreElements()) {
InputStream is = zf.getInputStream(zfe.nextElement());
refSet.add(new WeakReference<InputStream>(is, rq));
}
return refSet;
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4334846
@summary Make sure zip file comments of various sizes can be written.
@author Martin Buchholz
*/
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class Comment {
private static final String entryName = "entryName";
private static final String entryContents = "entryContents";
private static final int commentMaxLength = 0xFFFF;
private static final int [] commentLengths
= { 0, 1, 32768, commentMaxLength - 1, commentMaxLength };
public static void main(String argv[])
throws Exception
{
for (int i = 0; i < commentLengths.length; ++i) {
int commentLength = commentLengths[i];
String comment = buildComment(commentLength);
String name = "Test" + commentLength + ".zip";
writeZipFile(name, comment);
verifyZipFile(name, comment);
new File(name).delete();
System.out.println(commentLength + ": successful");
}
}
private static void writeZipFile(String name, String comment)
throws IOException
{
try (FileOutputStream fos = new FileOutputStream(name);
ZipOutputStream zos = new ZipOutputStream(fos))
{
zos.setComment(comment);
ZipEntry ze = new ZipEntry(entryName);
ze.setMethod(ZipEntry.DEFLATED);
zos.putNextEntry(ze);
new DataOutputStream(zos).writeUTF(entryContents);
zos.closeEntry();
}
}
private static void verifyZipFile(String name, String comment)
throws Exception
{
// Check that Zip entry was correctly written.
try (ZipFile zipFile = new ZipFile(name)) {
ZipEntry zipEntry = zipFile.getEntry(entryName);
InputStream is = zipFile.getInputStream(zipEntry);
String result = new DataInputStream(is).readUTF();
if (!result.equals(entryContents))
throw new Exception("Entry contents corrupted");
}
try (RandomAccessFile file = new RandomAccessFile(name, "r")) {
// Check that comment length was correctly written.
file.seek(file.length() - comment.length()
- ZipFile.ENDHDR + ZipFile.ENDCOM);
int b1 = file.readUnsignedByte();
int b2 = file.readUnsignedByte();
if (b1 + (b2 << 8) != comment.length())
throw new Exception("Zip file comment length corrupted");
// Check that comment was correctly written.
file.seek(file.length() - comment.length());
byte [] bytes = new byte [comment.length()];
file.readFully(bytes);
if (! comment.equals(new String(bytes, "UTF8")))
throw new Exception("Zip file comment corrupted");
}
}
private static String buildComment(int length) {
StringBuffer result = new StringBuffer();
while (result.length() < length) {
/* Endhdr is 22 bytes long, so this pattern makes it easy
* to see if it is copied correctly */
result.append("abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV");
}
String string = result.toString();
string = string.substring(string.length() - length);
return string;
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4842702
* @summary Check that constructors throw specified exceptions
* @author Martin Buchholz
*/
import java.util.zip.ZipFile;
import java.io.File;
import java.io.IOException;
public class Constructor {
private static void Unreached (Object o)
throws Exception
{
// Should never get here
throw new Exception ("Expected exception was not thrown");
}
public static void main(String[] args)
throws Exception
{
try { Unreached (new ZipFile ((File) null, ZipFile.OPEN_READ)); }
catch (NullPointerException e) {}
try { Unreached (new ZipFile ((File) null)); }
catch (NullPointerException e) {}
try { Unreached (new ZipFile ((String) null)); }
catch (NullPointerException e) {}
try { Unreached (new ZipFile ("NoSuchZip.zip")); }
catch (IOException e) {}
try { Unreached (new ZipFile (new File ("NoSuchZip.zip"))); }
catch (IOException e) {}
}
}

View file

@ -0,0 +1,332 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4770745 6218846 6218848 6237956 8313765 8316141
* @summary test for correct detection and reporting of corrupted zip files
* @author Martin Buchholz
* @run junit CorruptedZipFiles
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static java.util.zip.ZipFile.*;
import static org.junit.jupiter.api.Assertions.*;
public class CorruptedZipFiles {
/*
* Byte array holding a valid template ZIP.
*
* The 'good' ZIP file has the following structure:
*
* 0000 LOCAL HEADER #1 04034B50
* 0004 Extract Zip Spec 14 '2.0'
* 0005 Extract OS 00 'MS-DOS'
* 0006 General Purpose Flag 0808
* [Bits 1-2] 0 'Normal Compression'
* [Bit 3] 1 'Streamed'
* [Bit 11] 1 'Language Encoding'
* 0008 Compression Method 0008 'Deflated'
* 000A Last Mod Time 567F7D07 'Fri Mar 31 15:40:14 2023'
* 000E CRC 00000000
* 0012 Compressed Length 00000000
* 0016 Uncompressed Length 00000000
* 001A Filename Length 0001
* 001C Extra Length 0000
* 001E Filename 'x'
* 001F PAYLOAD ...
*
* 0022 STREAMING DATA HEADER 08074B50
* 0026 CRC 8CDC1683
* 002A Compressed Length 00000003
* 002E Uncompressed Length 00000001
*
* 0032 CENTRAL HEADER #1 02014B50
* 0036 Created Zip Spec 14 '2.0'
* 0037 Created OS 00 'MS-DOS'
* 0038 Extract Zip Spec 14 '2.0'
* 0039 Extract OS 00 'MS-DOS'
* 003A General Purpose Flag 0808
* [Bits 1-2] 0 'Normal Compression'
* [Bit 3] 1 'Streamed'
* [Bit 11] 1 'Language Encoding'
* 003C Compression Method 0008 'Deflated'
* 003E Last Mod Time 567F7D07 'Fri Mar 31 15:40:14 2023'
* 0042 CRC 8CDC1683
* 0046 Compressed Length 00000003
* 004A Uncompressed Length 00000001
* 004E Filename Length 0001
* 0050 Extra Length 0000
* 0052 Comment Length 0000
* 0054 Disk Start 0000
* 0056 Int File Attributes 0000
* [Bit 0] 0 'Binary Data'
* 0058 Ext File Attributes 00000000
* 005C Local Header Offset 00000000
* 0060 Filename 'x'
*
* 0061 END CENTRAL HEADER 06054B50
* 0065 Number of this disk 0000
* 0067 Central Dir Disk no 0000
* 0069 Entries in this disk 0001
* 006B Total Entries 0001
* 006D Size of Central Dir 0000002F
* 0071 Offset to Central Dir 00000032
* 0075 Comment Length 0000
*
*/
private static byte[] template;
// Copy of the template ZIP for modification by each test
private byte[] copy;
// Litte-endian ByteBuffer for manipulating the ZIP copy
private ByteBuffer buffer;
// Some well-known locations in the ZIP
private static int endpos, cenpos, locpos;
// The path used when reading/writing the corrupted ZIP to disk
private Path zip = Path.of("corrupted.zip");
/*
* Make a sample ZIP and calculate some known offsets into this ZIP
*/
@BeforeAll
public static void setup() throws IOException {
// Make a ZIP with a single entry
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(out)) {
ZipEntry e = new ZipEntry("x");
zos.putNextEntry(e);
zos.write((int)'x');
}
template = out.toByteArray();
// ByteBuffer for reading fields from the ZIP
ByteBuffer buffer = ByteBuffer.wrap(template).order(ByteOrder.LITTLE_ENDIAN);
// Calculate the offset of the End of central directory record
endpos = template.length - ENDHDR;
// Look up the offet of the Central directory header
cenpos = buffer.getShort(endpos + ENDOFF);
// Look up the offset of the corresponding Local file header
locpos = buffer.getShort(cenpos + CENOFF);
// Run some sanity checks on the valid ZIP:
assertEquals(ENDSIG, buffer.getInt(endpos),"Where's ENDSIG?");
assertEquals(CENSIG, buffer.getInt(cenpos),"Where's CENSIG?");
assertEquals(LOCSIG, buffer.getInt(locpos),"Where's LOCSIG?");
assertEquals(buffer.getShort(cenpos+CENNAM),
buffer.getShort(locpos+LOCNAM),
"Name field length mismatch");
assertEquals(buffer.getShort(cenpos+CENEXT),
buffer.getShort( locpos+LOCEXT),
"Extra field length mismatch");
}
/*
* Make a copy safe to modify by each test
*/
@BeforeEach
public void makeCopy() {
copy = template.clone();
buffer = ByteBuffer.wrap(copy).order(ByteOrder.LITTLE_ENDIAN);
}
/*
* Delete the ZIP file produced after each test method
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/*
* A ZipException is thrown when the 'End of Central Directory'
* (END) header has a CEN size exceeding past the offset of the END record
*/
@Test
public void excessiveCENSize() throws IOException {
buffer.putInt(endpos+ENDSIZ, 0xff000000);
assertZipException(".*bad central directory size.*");
}
/*
* A ZipException is thrown when the 'End of Central Directory'
* (END) header has a CEN offset with an invalid value.
*/
@Test
public void excessiveCENOffset() throws IOException {
buffer.putInt(endpos+ENDOFF, 0xff000000);
assertZipException(".*bad central directory offset.*");
}
/*
* A ZipException is thrown when a CEN header has an unexpected signature
*/
@Test
public void invalidCENSignature() throws IOException {
int existingSignature = buffer.getInt(cenpos);
buffer.putInt(cenpos, existingSignature +1);
assertZipException(".*bad signature.*");
}
/*
* A ZipException is thrown when a CEN header has the
* 'general purpose bit flag 0' ('encrypted') set.
*/
@Test
public void encryptedEntry() throws IOException {
copy[cenpos+CENFLG] |= 1;
assertZipException(".*encrypted entry.*");
}
/*
* A ZipException is thrown when a CEN header has a file name
* length which makes the CEN header overflow into the
* 'End of central directory' record.
*/
@Test
public void excessiveFileNameLength() throws IOException {
short existingNameLength = buffer.getShort(cenpos + CENNAM);
buffer.putShort(cenpos+CENNAM, (short) (existingNameLength + 1));
assertZipException(".*bad header size.*");
}
/*
* A ZipException is thrown when a CEN header has a
* file name length which makes the CEN header overflow into the
* 'End of central directory' record.
*/
@Test
public void excessiveFileNameLength2() throws IOException {
buffer.putShort(cenpos + CENNAM, (short) 0xfdfd);
assertZipException(".*bad header size.*");
}
/*
* A ZipException is thrown if the last CEN header is not immediately
* followed by the start of the 'End of central directory' record
*/
@Test
public void insufficientFilenameLength() throws IOException {
short existingNameLength = buffer.getShort(cenpos + CENNAM);
buffer.putShort(cenpos+CENNAM, (short) (existingNameLength - 1));
assertZipException(".*bad header size.*");
}
/*
* A ZipException is thrown if a CEN header has an
* extra field length which makes the CEN header overflow into the
* End of central directory record.
*/
@Test
public void excessiveExtraFieldLength() throws IOException {
buffer.put(cenpos+CENEXT, (byte) 0xff);
buffer.put(cenpos+CENEXT+1, (byte) 0xff);
assertZipException(".*bad header size.*");
}
/*
* A ZipException is thrown if a CEN header has an
* extra field length which makes the CEN header overflow into the
* End of central directory record.
*/
@Test
public void excessiveExtraFieldLength2() throws IOException {
buffer.putShort(cenpos+CENEXT, (short) 0xfdfd);
assertZipException(".*bad header size.*");
}
/*
* A ZipException is thrown when a CEN header has a comment length
* which overflows into the 'End of central directory' record
*/
@Test
public void excessiveCommentLength() throws IOException {
short existingCommentLength = buffer.getShort(cenpos + CENCOM);
buffer.putShort(cenpos+CENCOM, (short) (existingCommentLength + 1));
assertZipException(".*bad header size.*");
}
/*
* A ZipException is thrown when a CEN header has a
* compression method field which is unsupported by the implementation
*/
@Test
public void unsupportedCompressionMethod() throws IOException {
copy[cenpos+CENHOW] = 2;
assertZipException(".*bad compression method.*");
}
/*
* A ZipException is thrown when a LOC header has an unexpected signature
*/
@Test
public void invalidLOCSignature() throws IOException {
int existingSignatur = buffer.getInt(locpos);
buffer.putInt(locpos, existingSignatur +1);
assertZipException(".*bad signature.*");
}
/*
* Assert that opening a ZIP file and consuming the entry's
* InputStream using the ZipFile API fails with a ZipException
* with a message matching the given pattern.
*
* The ZIP file opened is the contents of the 'copy' byte array.
*/
void assertZipException(String msgPattern) throws IOException {
Files.write(zip, copy);
ZipException ex = assertThrows(ZipException.class, () -> {
try (ZipFile zf = new ZipFile(zip.toFile())) {
try (InputStream is = zf.getInputStream(new ZipEntry("x"))) {
is.transferTo(OutputStream.nullOutputStream());
}
}
});
assertTrue(ex.getMessage().matches(msgPattern),
"Unexpected ZipException message: " + ex.getMessage());
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import com.sun.net.httpserver.*;
public class DeleteTempJar
{
public static void realMain(String args[]) throws Exception
{
final File zf = File.createTempFile("deletetemp", ".jar");
zf.deleteOnExit();
try (FileOutputStream fos = new FileOutputStream(zf);
JarOutputStream jos = new JarOutputStream(fos))
{
JarEntry je = new JarEntry("entry");
jos.putNextEntry(je);
jos.write("hello, world".getBytes("ASCII"));
}
HttpServer server = HttpServer.create(
new InetSocketAddress((InetAddress) null, 0), 0);
HttpContext context = server.createContext("/",
new HttpHandler() {
public void handle(HttpExchange e) {
try (FileInputStream fis = new FileInputStream(zf)) {
e.sendResponseHeaders(200, zf.length());
OutputStream os = e.getResponseBody();
byte[] buf = new byte[1024];
int count = 0;
while ((count = fis.read(buf)) != -1) {
os.write(buf, 0, count);
}
} catch (Exception ex) {
unexpected(ex);
} finally {
e.close();
}
}
});
server.start();
try {
URL url = new URL("jar:http://localhost:"
+ new Integer(server.getAddress().getPort()).toString()
+ "/deletetemp.jar!/");
JarURLConnection c = (JarURLConnection)url.openConnection();
JarFile f = c.getJarFile();
check(f.getEntry("entry") != null);
System.out.println(f.getName());
} finally {
server.stop(0);
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static boolean pass() {passed++; return true;}
static boolean fail() {failed++; Thread.dumpStack(); return false;}
static boolean fail(String msg) {System.out.println(msg); return fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static boolean equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) return pass();
else return fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4166799
* @summary Make sure URL-downloaded jar files (jar_cache files)
* will be deleted when VM exits.
* @modules jdk.httpserver
* @library /test/lib
* @build jdk.test.lib.process.*
* DeleteTempJar
* @run main DeleteTempJarTest
*/
import java.nio.file.Files;
import java.nio.file.Paths;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.Asserts;
public class DeleteTempJarTest {
public static void main(String[] args) throws Exception {
String tmpFile = ProcessTools.executeTestJava(DeleteTempJar.class.getName())
.shouldHaveExitValue(0)
.getStdout();
Asserts.assertFalse(Files.exists(Paths.get(tmpFile.trim())));
}
}

View file

@ -0,0 +1,216 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HexFormat;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*
* @test
* @bug 8349914
* @summary Validate ZipFile::getFileInputStream returns the correct data when
* there are multiple entries with the same filename
* @run junit DupEntriesGetInputStream
*/
class DupEntriesGetInputStream {
// created through a call to createNormalZIP()
private static final String NORMAL_ZIP_CONTENT_HEX = """
504b03041400080808009195c35a00000000000000000000000006000000456e747279310bc9c8
2c560022d7bc92a24a054300504b07089bc0e55b0f0000000f000000504b030414000808080091
95c35a00000000000000000000000006000000456e747279320bc9c82c560022d7bc92a24a85bc
d2dca4d4228590f27c00504b0708ebda8deb1800000018000000504b03041400080808009195c3
5a00000000000000000000000006000000456e747279330bc9c82c560022d7bc92a24a05650563
00504b0708d1eafe7d1100000011000000504b010214001400080808009195c35a9bc0e55b0f00
00000f000000060000000000000000000000000000000000456e74727931504b01021400140008
0808009195c35aebda8deb1800000018000000060000000000000000000000000043000000456e
74727932504b010214001400080808009195c35ad1eafe7d110000001100000006000000000000
000000000000008f000000456e74727933504b050600000000030003009c000000d40000000000
""";
// intentionally unused but left here to allow for constructing newer/updated
// NORMAL_ZIP_CONTENT_HEX, when necessary
private static String createNormalZIP() throws IOException {
final ByteArrayOutputStream zipContent = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(zipContent)) {
zos.putNextEntry(new ZipEntry(ENTRY1_NAME));
zos.write(ENTRY1.getBytes(US_ASCII));
zos.putNextEntry(new ZipEntry(ENTRY2_NAME));
zos.write(ENTRY2.getBytes(US_ASCII));
zos.putNextEntry(new ZipEntry(ENTRY3_NAME));
zos.write(ENTRY3.getBytes(US_ASCII));
}
return HexFormat.of().formatHex(zipContent.toByteArray());
}
// Entry Names and their data to be added to the ZIP File
private static final String ENTRY1_NAME = "Entry1";
private static final String ENTRY1 = "This is Entry 1";
private static final String ENTRY2_NAME = "Entry2";
private static final String ENTRY2 = "This is Entry number Two";
private static final String ENTRY3_NAME = "Entry3";
private static final String ENTRY3 = "This is Entry # 3";
// ZIP entry and its expected data
record ZIP_ENTRY(String entryName, String data) {
}
private static final ZIP_ENTRY[] ZIP_ENTRIES = new ZIP_ENTRY[]{
new ZIP_ENTRY(ENTRY1_NAME, ENTRY1),
new ZIP_ENTRY(ENTRY2_NAME, ENTRY2),
new ZIP_ENTRY(ENTRY1_NAME, ENTRY3)
};
private static Path dupEntriesZipFile;
// 008F LOCAL HEADER #3 04034B50
// ...
// 00A1 Compressed Length 00000000
// 00A5 Uncompressed Length 00000000
// 00A9 Filename Length 0006
// ...
// 00AD Filename 'Entry3'
private static final int ENTRY3_FILENAME_LOC_OFFSET = 0x00AD;
// 013C CENTRAL HEADER #3 02014B50
// ...
// 0150 Compressed Length 00000011
// 0154 Uncompressed Length 00000011
// 0158 Filename Length 0006
// ...
// 016A Filename 'Entry3'
private static final int ENTRY3_FILENAME_CEN_OFFSET = 0x016A;
@BeforeAll
static void createDupEntriesZIP() throws Exception {
final byte[] originalZIPContent = HexFormat.of().parseHex(
NORMAL_ZIP_CONTENT_HEX.replace("\n", ""));
final ByteBuffer buf = ByteBuffer.wrap(originalZIPContent).order(LITTLE_ENDIAN);
// replace the file name literal "Entry3" with the literal "Entry1", both in the
// LOC header and the CEN of Entry3
final int locEntry3LastCharOffset = ENTRY3_FILENAME_LOC_OFFSET + ENTRY3_NAME.length() - 1;
buf.put(locEntry3LastCharOffset, (byte) 49); // 49 represents the character "1"
final int cenEntry3LastCharOffset = ENTRY3_FILENAME_CEN_OFFSET + ENTRY3_NAME.length() - 1;
buf.put(cenEntry3LastCharOffset, (byte) 49); // 49 represents the character "1"
buf.rewind();
// write out the manipulated ZIP content, containing duplicate entries, into a file
// so that it can be read using ZipFile
dupEntriesZipFile = Files.createTempFile(Path.of("."), "8349914-", ".zip");
Files.write(dupEntriesZipFile, buf.array());
System.out.println("created ZIP file with duplicate entries at " + dupEntriesZipFile);
}
/*
* Validate that the correct ZipEntry data is returned when a List is used
* to access entries returned from ZipFile::entries
*
* @throws IOException if an error occurs
*/
@Test
public void testUsingListEntries() throws IOException {
System.out.println("Processing entries via Collections.list()");
try (ZipFile zf = new ZipFile(dupEntriesZipFile.toFile())) {
var entryNumber = 0;
for (var e : Collections.list(zf.entries())) {
verifyEntry(entryNumber++, zf, e);
}
}
}
/*
* Validate that the correct ZipEntry data is returned when a ZipEntryIterator
* is used to access entries returned from ZipFile::entries
*
* @throws IOException if an error occurs
*/
@Test
public void testUsingZipEntryIterator() throws IOException {
System.out.println("Processing entries via a ZipEntryIterator");
try (ZipFile zf = new ZipFile(dupEntriesZipFile.toFile())) {
Enumeration<? extends ZipEntry> entries = zf.entries();
var entryNumber = 0;
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
verifyEntry(entryNumber++, zf, entry);
}
}
}
/*
* Validate that the correct ZipEntry data is returned when a EntrySpliterator
* is used to access entries returned from ZipFile::stream
*
* @throws IOException if an error occurs
*/
@Test
public void testUsingEntrySpliterator() throws IOException {
System.out.println("Processing entries via a EntrySpliterator");
AtomicInteger eNumber = new AtomicInteger(0);
try (ZipFile zf = new ZipFile(dupEntriesZipFile.toFile())) {
zf.stream().forEach(e -> {
try {
verifyEntry(eNumber.getAndIncrement(), zf, e);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
});
}
}
/*
* Verify the ZipEntry returned matches what is expected
*
* @param entryNumber offset into ZIP_ENTRIES containing the expected value
* to be returned
* @param zf ZipFile containing the entry
* @param e ZipEntry to validate
* @throws IOException
*/
private static void verifyEntry(int entryNumber, ZipFile zf, ZipEntry e) throws IOException {
System.out.println("Validating Entry: " + entryNumber);
assertEquals(ZIP_ENTRIES[entryNumber].entryName(), e.getName());
try (var in = zf.getInputStream(e)) {
var entryData = new String(in.readAllBytes(), StandardCharsets.UTF_8);
assertEquals(ZIP_ENTRIES[entryNumber].data(), entryData);
}
}
}

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8272746
* @modules java.base/jdk.internal.util
* @summary Verify that ZipFile rejects files with CEN sizes exceeding the implementation limit
* @library /test/lib
* @build jdk.test.lib.util.ZipUtils
* @run junit/othervm EndOfCenValidation
*/
import jdk.internal.util.ArraysSupport;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import static jdk.test.lib.util.ZipUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* This test augments {@link TestTooManyEntries}. It creates sparse ZIPs where
* the CEN size is inflated to the desired value. This helps this test run
* fast with much less resources.
*
* While the CEN in these files are zero-filled and the produced ZIPs are technically
* invalid, the CEN is never actually read by ZipFile since it does
* 'End of central directory record' (END header) validation before reading the CEN.
*/
public class EndOfCenValidation {
// Zip files produced by this test
static final Path CEN_TOO_LARGE_ZIP = Path.of("cen-size-too-large.zip");
static final Path INVALID_CEN_SIZE = Path.of("invalid-zen-size.zip");
static final Path BAD_CEN_OFFSET_ZIP = Path.of("bad-cen-offset.zip");
static final Path BAD_ENTRY_COUNT_ZIP = Path.of("bad-entry-count.zip");
// Maximum allowed CEN size allowed by ZipFile
static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH;
/**
* Delete big files after test, in case the file system did not support sparse files.
* @throws IOException if an error occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(CEN_TOO_LARGE_ZIP);
Files.deleteIfExists(INVALID_CEN_SIZE);
Files.deleteIfExists(BAD_CEN_OFFSET_ZIP);
Files.deleteIfExists(BAD_ENTRY_COUNT_ZIP);
}
/**
* Validates that an 'End of central directory record' (END header) with a CEN
* length exceeding {@link #MAX_CEN_SIZE} limit is rejected
* @throws IOException if an error occurs
*/
@Test
public void shouldRejectTooLargeCenSize() throws IOException {
int size = MAX_CEN_SIZE + 1;
Path zip = zipWithModifiedEndRecord(size, true, 0, CEN_TOO_LARGE_ZIP);
verifyRejection(zip, INVALID_CEN_SIZE_TOO_LARGE);
}
/**
* Validate that an 'End of central directory record' (END header)
* where the value of the CEN size field exceeds the position of
* the END header is rejected.
* @throws IOException if an error occurs
*/
@Test
public void shouldRejectInvalidCenSize() throws IOException {
int size = MAX_CEN_SIZE;
Path zip = zipWithModifiedEndRecord(size, false, 0, INVALID_CEN_SIZE);
verifyRejection(zip, INVALID_CEN_BAD_SIZE);
}
/**
* Validate that an 'End of central directory record' (the END header)
* where the value of the CEN offset field is larger than the position
* of the END header minus the CEN size is rejected
* @throws IOException if an error occurs
*/
@Test
public void shouldRejectInvalidCenOffset() throws IOException {
int size = MAX_CEN_SIZE;
Path zip = zipWithModifiedEndRecord(size, true, 100, BAD_CEN_OFFSET_ZIP);
verifyRejection(zip, INVALID_CEN_BAD_OFFSET);
}
/**
* Validate that a 'Zip64 End of Central Directory' record (the END header)
* where the value of the 'total entries' field is larger than what fits
* in the CEN size is rejected.
*
* @throws IOException if an error occurs
*/
@ParameterizedTest
@ValueSource(longs = {
-1, // Negative
Long.MIN_VALUE, // Very negative
0x3B / 3L - 1, // Cannot fit in test ZIP's CEN
MAX_CEN_SIZE / 3 + 1, // Too large to allocate int[] entries array
Long.MAX_VALUE // Unreasonably large
})
public void shouldRejectBadTotalEntries(long totalEntries) throws IOException {
Path zip = zip64WithModifiedTotalEntries(BAD_ENTRY_COUNT_ZIP, totalEntries);
verifyRejection(zip, INVALID_BAD_ENTRY_COUNT);
}
/**
* Verify that ZipFile rejects the ZIP file with a ZipException
* with the given message
* @param zip ZIP file to open
* @param msg exception message to expect
*/
private static void verifyRejection(Path zip, String msg) {
ZipException ex = assertThrows(ZipException.class, () -> {
new ZipFile(zip.toFile());
});
assertEquals(msg, ex.getMessage());
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4290060
@summary Check if the zip file is closed before access any
elements in the Enumeration.
@run junit EnumerateAfterClose
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class EnumerateAfterClose {
// ZIP file used in this test
private Path zip = Path.of("enum-after-close.zip");
/**
* Create a sample ZIP file for use by this test
* @throws IOException if an unexpected IOException occurs
*/
@BeforeEach
public void setUp() throws IOException {
try (OutputStream out = Files.newOutputStream(zip);
ZipOutputStream zo = new ZipOutputStream(out)) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
}
}
/**
* Delete the ZIP file produced by this test
* @throws IOException if an unexpected IOException occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/**
* Attempting to using a ZipEntry Enumeration after its backing
* ZipFile is closed should throw IllegalStateException.
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void enumeratingAfterCloseShouldThrowISE() throws IOException {
// Retain a reference to an enumeration backed by a closed ZipFile
Enumeration e;
try (ZipFile zf = new ZipFile(zip.toFile())) {
e = zf.entries();
}
// Using the enumeration after the ZipFile is closed should throw ISE
assertThrows(IllegalStateException.class, () -> {
if (e.hasMoreElements()) {
ZipEntry ze = (ZipEntry)e.nextElement();
}
});
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 7003462
@summary Make sure cached Inflater does not get finalized.
@run junit FinalizeInflater
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class FinalizeInflater {
// ZIP file produced by this test
private Path zip = Path.of("finalize-inflater.zip");
/**
* Create the sample ZIP used in this test
*
* @throws IOException if an unexpected IOException occurs
*/
@BeforeEach
public void setUp() throws IOException {
try (OutputStream out = Files.newOutputStream(zip);
ZipOutputStream zo = new ZipOutputStream(out)) {
zo.putNextEntry(new ZipEntry("file.txt"));
byte[] hello = "hello".getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < 100; i++) {
zo.write(hello);
}
}
}
/**
* Delete the ZIP file produced by this test
*
* @throws IOException if an unexpected IOException occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/**
* A cached Inflater should not be made invalid by finalization
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void shouldNotFinalizeInflaterInPool() throws IOException {
try (ZipFile zf = new ZipFile(zip.toFile())) {
ZipEntry ze = zf.getEntry("file.txt");
read(zf.getInputStream(ze));
System.gc();
System.runFinalization();
System.gc();
// read again
read(zf.getInputStream(ze));
}
}
private static void read(InputStream is)
throws IOException
{
Wrapper wrapper = new Wrapper(is);
is.readAllBytes();
}
static class Wrapper {
InputStream is;
public Wrapper(InputStream is) {
this.is = is;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
is.close();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary Verify that ZipFile::size reports the actual number of entries
* even with an incorrect ENDTOT field
* @run junit IncorrectEndTot
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.util.Collections;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class IncorrectEndTot {
// File to use for this test
File file = new File("incorrect-end-centot.zip");
// Return scenarios for correct and incorrect ENDTOT fields
public static Stream<Arguments> scenarios() {
return Stream.of(
Arguments.of(10, 10), // CEN agrees with ENDTOT
Arguments.of(10, 11), // CEN has one less than ENDTOT
Arguments.of(11, 10), // CEN has one more than ENDTOT
Arguments.of(0, 0), // Empty ZIP, correct ENDTOT
Arguments.of(0, 10) // Empty ZIP, incorrect ENDTOT
);
}
/**
* Delete the file used by this test
*/
@AfterEach
public void cleanup() {
file.delete();
}
/**
* Verify that ZipFile::size reports the actual number of CEN records,
* regardless of the ENDTOT field.
*
* @param actual number of entries in the ZIP file
* @param reported number reported in ENDTOT
* @throws IOException if an unexpected error occurs
*/
@ParameterizedTest
@MethodSource("scenarios")
public void shouldCountActualEntries(int actual, int reported) throws IOException {
createZip(file, actual, reported);
try (ZipFile zf = new ZipFile(file)) {
assertEquals(actual, zf.size());
assertEquals(actual, Collections.list(zf.entries()).size());
assertEquals(actual, zf.stream().count());
}
}
/**
* Create a ZIP file with a number of entries, possibly reporting an incorrect number in
* the ENDTOT field
* @param file the file to write to
* @param numEntries the number of entries to generate
* @param reported the number of entries to report in the END header's ENDTOT field
* @throws IOException
*/
private static void createZip(File file, int numEntries, int reported) throws IOException {
// Create a ZIP
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ZipOutputStream zo = new ZipOutputStream(out)) {
for (int i = 0; i < numEntries; i++) {
zo.putNextEntry(new ZipEntry("entry_" + i));
}
}
byte[] bytes = out.toByteArray();
// Update the ENDTOT field to report a possibly incorrect number
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
buf.putShort(bytes.length - ZipFile.ENDHDR + ZipFile.ENDTOT, (short) reported);
// Write to disk
Files.write(file.toPath(), bytes);
}
}

View file

@ -0,0 +1,328 @@
/*
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Formatter;
import java.util.stream.Stream;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* @test
* @bug 8301873 8321156
* @summary Validate that a ZipException is thrown when a ZIP file with
* invalid UTF-8 byte sequences in the name or comment fields is opened via
* ZipFile or traversed via ZipInputStream.
* Also validate that ZipFile::getComment will return null with invalid UTF-8
* byte sequences in the ZIP file comment
* @run junit InvalidBytesInEntryNameOrComment
*/
public class InvalidBytesInEntryNameOrComment {
// Zip file that is created and used by the test
public static final Path ZIP_FILE = Path.of("BadName.zip");
// Example invalid UTF-8 byte sequence
private static final byte[] INVALID_UTF8_BYTE_SEQUENCE = {(byte) 0xF0, (byte) 0xA4, (byte) 0xAD};
// Expected error message when an invalid entry name or entry comment is
// encountered when accessing a CEN Header
private static final String CEN_BAD_ENTRY_NAME_OR_COMMENT = "invalid CEN header (bad entry name or comment)";
// Expected error message when an invalid entry name is encountered when
// accessing a LOC Header
private static final String LOC_HEADER_BAD_ENTRY_NAME = "invalid LOC header (bad entry name)";
// Zip file comment starting offset
private static final int ZIP_FILE_COMMENT_OFFSET = 0x93;
// CEN Header offset for the entry comment to be modified
private static final int CEN_FILE_HEADER_FILE_COMMENT_STARTING_OFFSET = 0x6D;
// CEN Header offset for the entry name to be modified
private static final int CEN_FILE_HEADER_FILENAME_STARTING_OFFSET = 0x66;
// LOC Header offset for the entry name to be modified
private static final int LOC_FILE_HEADER_FILENAME_STARTING_OFFSET = 0x1e;
// CEN Entry comment
public static final String ENTRY_COMMENT = "entryComment";
// Entry name to be modified/validated
public static final String ENTRY_NAME = "entryName";
// Zip file comment to be modified/validated
public static final String ZIP_FILE_COMMENT = "ZipFileComment";
// Buffer used to massage the byte array containing the Zip File
private ByteBuffer buffer;
// Array used to copy VALID_ZIP into prior to each test run
private byte[] zipArray;
/**
* Byte array representing a valid Zip file prior modifying the CEN/LOC
* entry name, CEN entry comment or Zip file comment with an invalid
* UTF-8 byte sequence.
* See the createZipByteArray method which was used to create the original
* Zip file
* ----------------#1--------------------
* [Central Directory Header]
* 0x3a: Signature : 0x02014b50
* 0x3e: Created Zip Spec : 0x14 [2.0]
* 0x3f: Created OS : 0x0 [MS-DOS]
* 0x40: VerMadeby : 0x14 [0, 2.0]
* 0x41: VerExtract : 0x14 [2.0]
* 0x42: Flag : 0x808
* 0x44: Method : 0x8 [DEFLATED]
* 0x46: Last Mod Time : 0x58506664 [Fri Feb 16 12:51:08 EST 2024]
* 0x4a: CRC : 0xd202ef8d
* 0x4e: Compressed Size : 0x3
* 0x52: Uncompressed Size: 0x1
* 0x56: Name Length : 0x9
* 0x58: Extra Length : 0x0
* 0x5a: Comment Length : 0xc
* 0x5c: Disk Start : 0x0
* 0x5e: Attrs : 0x0
* 0x60: AttrsEx : 0x0
* 0x64: Loc Header Offset: 0x0
* 0x68: File Name : entryName
* 0x71: Comment : [entryComment]
* [Local File Header]
* 0x0: Signature : 0x04034b50
* 0x4: Version : 0x14 [2.0]
* 0x6: Flag : 0x808
* 0x8: Method : 0x8 [DEFLATED]
* 0xa: LastMTime : 0x58506664 [Fri Feb 16 12:51:08 EST 2024]
* 0xe: CRC : 0x0
* 0x12: CSize : 0x0
* 0x16: Size : 0x0
* 0x1a: Name Length : 0x9 [entryName]
* 0x1c: ExtraLength : 0x0
* 0x1e: File Name : [entryName]
* [End Central Directory Header]
* 0x7d: Signature : 0x06054b50
* 0x85: Disk Entries: 0x1
* 0x87: Total Entries: 0x1
* 0x89: CEN Size : 0x43
* 0x8d: Offset CEN : 0x3a
* 0x91: Comment Len : 0xe
* 0x93: Comment : [ZipFileComment]
*/
public static byte[] VALID_ZIP = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x7d, (byte) 0x6f, (byte) 0x50, (byte) 0x58, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x65, (byte) 0x6e,
(byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x4e, (byte) 0x61, (byte) 0x6d, (byte) 0x65, (byte) 0x63,
(byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7, (byte) 0x8, (byte) 0x8d, (byte) 0xef,
(byte) 0x2, (byte) 0xd2, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, (byte) 0x0,
(byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x7d, (byte) 0x6f,
(byte) 0x50, (byte) 0x58, (byte) 0x8d, (byte) 0xef, (byte) 0x2, (byte) 0xd2, (byte) 0x3, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x4e, (byte) 0x61, (byte) 0x6d,
(byte) 0x65, (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x43, (byte) 0x6f,
(byte) 0x6d, (byte) 0x6d, (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x50, (byte) 0x4b, (byte) 0x5,
(byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x1,
(byte) 0x0, (byte) 0x43, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3a, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0xe, (byte) 0x0, (byte) 0x5a, (byte) 0x69, (byte) 0x70, (byte) 0x46, (byte) 0x69,
(byte) 0x6c, (byte) 0x65, (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x6d, (byte) 0x65, (byte) 0x6e,
(byte) 0x74,
};
/**
* Delete the Zip file if it exists prior to each run and create a copy
* of the byte array representing a valid ZIP file to be used by each test run
*
* @throws IOException if an error occurs
*/
@BeforeEach
public void setupTest() throws IOException {
Files.deleteIfExists(ZIP_FILE);
zipArray = Arrays.copyOf(VALID_ZIP, VALID_ZIP.length);
buffer = ByteBuffer.wrap(zipArray).order(ByteOrder.LITTLE_ENDIAN);
}
/**
* The MethodSource of CEN offsets to modify with an invalid UTF-8 byte
* sequence
*
* @return Arguments used in each test run
*/
private static Stream<Arguments> CENCommentOffsets() {
return Stream.of(
// Entry's name starting offset
Arguments.of(CEN_FILE_HEADER_FILENAME_STARTING_OFFSET),
// Entry's comment starting offset
Arguments.of(CEN_FILE_HEADER_FILE_COMMENT_STARTING_OFFSET)
);
}
/**
* Validate that the original Zip file can be opened via ZipFile.
* @throws IOException if an error occurs
*/
@Test
public void testValidEntryNameAndComment() throws IOException {
Files.write(ZIP_FILE, zipArray);
try (ZipFile zf = new ZipFile(ZIP_FILE.toFile())) {
var comment = zf.getComment();
assertEquals(ZIP_FILE_COMMENT, comment);
}
}
/**
* Validate that the original Zip file can be opened and traversed via
* ZipinputStream::getNextEntry.
* @throws IOException if an error occurs
*/
@Test
public void traverseZipWithZipInputStreamTest() throws IOException {
Files.write(ZIP_FILE, zipArray);
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(ZIP_FILE.toFile()))) {
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
assertEquals(ENTRY_NAME, ze.getName());
}
}
}
/**
* Validate that a ZipException is thrown when an entry name or entry comment
* within a CEN file header contains an invalid UTF-8 byte sequence.
*
* @param offset the offset to the file name or file comment within the CEN
* file header
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("CENCommentOffsets")
public void testInValidEntryNameOrComment(int offset)
throws IOException {
createInvalidUTFEntryInZipFile(offset);
Throwable ex = assertThrows(ZipException.class, () ->
{
try (ZipFile zf = new ZipFile(ZIP_FILE.toFile())) {};
}
);
assertEquals(CEN_BAD_ENTRY_NAME_OR_COMMENT, ex.getMessage());
}
/**
* Validate that a null is returned from ZipFile::getComment when the
* comment contains an invalid UTF-8 byte sequence.
* @throws IOException if an error occurs
*/
@Test
public void testInValidZipFileComment() throws IOException {
createInvalidUTFEntryInZipFile(ZIP_FILE_COMMENT_OFFSET);
try (ZipFile zf = new ZipFile(ZIP_FILE.toFile())) {
assertNull(zf.getComment());
}
}
/**
* Validate that a ZipException is thrown when an entry name
* within a LOC file header contains an invalid UTF-8 byte sequence.
* @throws IOException if an error occurs
*/
@Test
public void invalidZipInputStreamTest() throws IOException {
createInvalidUTFEntryInZipFile(LOC_FILE_HEADER_FILENAME_STARTING_OFFSET);
Throwable ex = assertThrows(ZipException.class, () ->
{
try (ZipInputStream zis =
new ZipInputStream(new FileInputStream(ZIP_FILE.toFile()))) {
zis.getNextEntry();
};
});
assertEquals(LOC_HEADER_BAD_ENTRY_NAME, ex.getMessage());
}
/**
* Utility method which modifies a Zip file starting at the specified
* offset to include an invalid UTF-8 byte sequence.
*
* @param offset starting offset within the Zip file to modify
* @throws IOException if an error occurs
*/
private void createInvalidUTFEntryInZipFile(int offset) throws IOException {
buffer.put(offset, INVALID_UTF8_BYTE_SEQUENCE, 0,
INVALID_UTF8_BYTE_SEQUENCE.length);
Files.write(ZIP_FILE, zipArray);
}
/**
* Utility method which creates the Zip file used by the tests and
* converts Zip file to byte array declaration.
*
* @throws IOException if an error occurs
*/
private void createZipByteArray() throws IOException {
ZipOutputStream zos = new ZipOutputStream(
new FileOutputStream(ZIP_FILE.toFile()));
zos.setComment(ZIP_FILE_COMMENT);
ZipEntry entry = new ZipEntry(ENTRY_NAME);
entry.setComment(ENTRY_COMMENT);
zos.putNextEntry(entry);
zos.write(new byte[1]);
zos.closeEntry();
zos.close();
// Now create the byte array entry declaration
var fooJar = Files.readAllBytes(ZIP_FILE);
var result = createByteArray(fooJar, "VALID_ZIP");
System.out.println(result);
}
/**
* Utility method which takes a byte array and converts to byte array
* declaration. For example:
* {@snippet :
* var fooJar = Files.readAllBytes(Path.of("foo.jar"));
* var result = createByteArray(fooJar,"FOOBYTES");
* System.out.println(result);
* }
*
* @param bytes A byte array used to create a byte array declaration
* @param name Name to be used in the byte array declaration
* @return The formatted byte array declaration
*/
public static String createByteArray(byte[] bytes, String name) {
StringBuilder sb = new StringBuilder(bytes.length * 5);
Formatter fmt = new Formatter(sb);
fmt.format(" public static byte[] %s = {", name);
final int linelen = 8;
for (int i = 0; i < bytes.length; i++) {
if (i % linelen == 0) {
fmt.format("%n ");
}
fmt.format(" (byte) 0x%x,", bytes[i] & 0xff);
}
fmt.format("%n };%n");
return sb.toString();
}
}

View file

@ -0,0 +1,348 @@
/*
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @test
* @bug 8280404
* @summary Validate that Zip/JarFile will throw a ZipException when the CEN
* comment length field contains an incorrect value
* @run junit/othervm InvalidCommentLengthTest
*/
public class InvalidCommentLengthTest {
// Name used to create a JAR with an invalid comment length
public static final Path INVALID_CEN_COMMENT_LENGTH_JAR =
Path.of("Invalid-CEN-Comment-Length.jar");
// Name used to create a JAR with a valid comment length
public static final Path VALID_CEN_COMMENT_LENGTH_JAR =
Path.of("Valid-CEN-Comment-Length.jar");
// Zip/Jar CEN file header entry that will be modified
public static final String META_INF_MANIFEST_MF = "META-INF/MANIFEST.MF";
// Expected ZipException message when the comment length corrupts the
// Zip/Jar file
public static final String INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT =
"invalid CEN header (bad entry name or comment)";
/**
* Byte array representing a valid jar file prior modifying the comment length
* entry in a CEN file header.
* The "Valid-CEN-Comment-Length.jar" jar file was created via:
* <pre>
* {@code
* jar cvf Valid-CEN-Comment-Length.jar Hello.txt Tennis.txt BruceWayne.txt
* added manifest
* adding: Hello.txt(in = 12) (out= 14)(deflated -16%)
* adding: Tennis.txt(in = 53) (out= 53)(deflated 0%)
* adding: BruceWayne.txt(in = 12) (out= 14)(deflated -16%)
* }
* </pre>
* Its contents are:
* <pre>
* {@code
* jar tvf Valid-CEN-Comment-Length.jar
* 0 Wed Mar 02 06:39:24 EST 2022 META-INF/
* 66 Wed Mar 02 06:39:24 EST 2022 META-INF/MANIFEST.MF
* 12 Wed Mar 02 06:39:06 EST 2022 Hello.txt
* 53 Wed Mar 02 13:04:48 EST 2022 Tennis.txt
* 12 Wed Mar 02 15:15:34 EST 2022 BruceWayne.txt
* }
* </pre>
* The ByteArray was created by:
* <pre>
* {@code
* var jar = Files.readAllBytes("Valid-CEN-Comment-Length.jar");
* var validEntryName = createByteArray(fooJar,
* "VALID_ZIP_WITH_NO_COMMENTS_BYTES");
* }
* </pre>
*/
public static byte[] VALID_ZIP_WITH_NO_COMMENTS_BYTES = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14,
(byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8, (byte) 0x0,
(byte) 0xec, (byte) 0x34, (byte) 0x62, (byte) 0x54, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x4, (byte) 0x0,
(byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d,
(byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0xfe,
(byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x3, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x7, (byte) 0x8, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4,
(byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0xec, (byte) 0x34, (byte) 0x62, (byte) 0x54,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41,
(byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f,
(byte) 0x4d, (byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46,
(byte) 0x45, (byte) 0x53, (byte) 0x54, (byte) 0x2e, (byte) 0x4d,
(byte) 0x46, (byte) 0xf3, (byte) 0x4d, (byte) 0xcc, (byte) 0xcb,
(byte) 0x4c, (byte) 0x4b, (byte) 0x2d, (byte) 0x2e, (byte) 0xd1,
(byte) 0xd, (byte) 0x4b, (byte) 0x2d, (byte) 0x2a, (byte) 0xce,
(byte) 0xcc, (byte) 0xcf, (byte) 0xb3, (byte) 0x52, (byte) 0x30,
(byte) 0xd4, (byte) 0x33, (byte) 0xe0, (byte) 0xe5, (byte) 0x72,
(byte) 0x2e, (byte) 0x4a, (byte) 0x4d, (byte) 0x2c, (byte) 0x49,
(byte) 0x4d, (byte) 0xd1, (byte) 0x75, (byte) 0xaa, (byte) 0x4,
(byte) 0xa, (byte) 0x98, (byte) 0xe8, (byte) 0x19, (byte) 0xe8,
(byte) 0x19, (byte) 0x2a, (byte) 0x68, (byte) 0xf8, (byte) 0x17,
(byte) 0x25, (byte) 0x26, (byte) 0xe7, (byte) 0xa4, (byte) 0x2a,
(byte) 0x38, (byte) 0xe7, (byte) 0x17, (byte) 0x15, (byte) 0xe4,
(byte) 0x17, (byte) 0x25, (byte) 0x96, (byte) 0x0, (byte) 0x15,
(byte) 0x6b, (byte) 0xf2, (byte) 0x72, (byte) 0xf1, (byte) 0x72,
(byte) 0x1, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7,
(byte) 0x8, (byte) 0xf4, (byte) 0x59, (byte) 0xdc, (byte) 0xa6,
(byte) 0x42, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x42,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b,
(byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8,
(byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0xe3, (byte) 0x34,
(byte) 0x62, (byte) 0x54, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74,
(byte) 0x78, (byte) 0x74, (byte) 0xf3, (byte) 0x48, (byte) 0xcd,
(byte) 0xc9, (byte) 0xc9, (byte) 0x57, (byte) 0x28, (byte) 0xcf,
(byte) 0x2f, (byte) 0xca, (byte) 0x49, (byte) 0xe1, (byte) 0x2,
(byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7, (byte) 0x8,
(byte) 0xd5, (byte) 0xe0, (byte) 0x39, (byte) 0xb7, (byte) 0xe,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xc, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3,
(byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x98, (byte) 0x68, (byte) 0x62,
(byte) 0x54, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x54, (byte) 0x65, (byte) 0x6e,
(byte) 0x6e, (byte) 0x69, (byte) 0x73, (byte) 0x2e, (byte) 0x74,
(byte) 0x78, (byte) 0x74, (byte) 0x73, (byte) 0xf2, (byte) 0xb,
(byte) 0x50, (byte) 0x8, (byte) 0x48, (byte) 0x2c, (byte) 0xca,
(byte) 0x4c, (byte) 0x4a, (byte) 0x2c, (byte) 0x56, (byte) 0xf0,
(byte) 0x2f, (byte) 0x48, (byte) 0xcd, (byte) 0x53, (byte) 0xc8,
(byte) 0x2c, (byte) 0x56, (byte) 0x48, (byte) 0x54, (byte) 0x48,
(byte) 0x2b, (byte) 0xcd, (byte) 0x53, (byte) 0x8, (byte) 0x49,
(byte) 0xcd, (byte) 0xcb, (byte) 0x3, (byte) 0x72, (byte) 0x42,
(byte) 0xf2, (byte) 0x4b, (byte) 0x8b, (byte) 0xf2, (byte) 0x12,
(byte) 0x73, (byte) 0x53, (byte) 0xf3, (byte) 0x4a, (byte) 0x14,
(byte) 0x4a, (byte) 0xf2, (byte) 0x15, (byte) 0xca, (byte) 0x13,
(byte) 0x4b, (byte) 0x92, (byte) 0x33, (byte) 0xb8, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x7, (byte) 0x8, (byte) 0xaa,
(byte) 0xad, (byte) 0x14, (byte) 0xd, (byte) 0x35, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x35, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4,
(byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0xf1, (byte) 0x79, (byte) 0x62, (byte) 0x54,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xe, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x42, (byte) 0x72, (byte) 0x75, (byte) 0x63,
(byte) 0x65, (byte) 0x57, (byte) 0x61, (byte) 0x79, (byte) 0x6e,
(byte) 0x65, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74,
(byte) 0xf3, (byte) 0x54, (byte) 0x48, (byte) 0xcc, (byte) 0x55,
(byte) 0x70, (byte) 0x4a, (byte) 0x2c, (byte) 0xc9, (byte) 0x4d,
(byte) 0xcc, (byte) 0xe3, (byte) 0x2, (byte) 0x0, (byte) 0x50,
(byte) 0x4b, (byte) 0x7, (byte) 0x8, (byte) 0x6c, (byte) 0x70,
(byte) 0x60, (byte) 0xbd, (byte) 0xe, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0xec, (byte) 0x34, (byte) 0x62,
(byte) 0x54, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x2, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0,
(byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41,
(byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f,
(byte) 0xfe, (byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x50,
(byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, (byte) 0x0,
(byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0xec, (byte) 0x34, (byte) 0x62, (byte) 0x54,
(byte) 0xf4, (byte) 0x59, (byte) 0xdc, (byte) 0xa6, (byte) 0x42,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x42, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x3d, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d,
(byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0x4d,
(byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45,
(byte) 0x53, (byte) 0x54, (byte) 0x2e, (byte) 0x4d, (byte) 0x46,
(byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0xe3, (byte) 0x34, (byte) 0x62,
(byte) 0x54, (byte) 0xd5, (byte) 0xe0, (byte) 0x39, (byte) 0xb7,
(byte) 0xe, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xc,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xc1, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c,
(byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74,
(byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x98, (byte) 0x68, (byte) 0x62,
(byte) 0x54, (byte) 0xaa, (byte) 0xad, (byte) 0x14, (byte) 0xd,
(byte) 0x35, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x35,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x6, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x54, (byte) 0x65, (byte) 0x6e, (byte) 0x6e,
(byte) 0x69, (byte) 0x73, (byte) 0x2e, (byte) 0x74, (byte) 0x78,
(byte) 0x74, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2,
(byte) 0x14, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x8,
(byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0xf1, (byte) 0x79,
(byte) 0x62, (byte) 0x54, (byte) 0x6c, (byte) 0x70, (byte) 0x60,
(byte) 0xbd, (byte) 0xe, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xe,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x73, (byte) 0x1,
(byte) 0x0, (byte) 0x0, (byte) 0x42, (byte) 0x72, (byte) 0x75,
(byte) 0x63, (byte) 0x65, (byte) 0x57, (byte) 0x61, (byte) 0x79,
(byte) 0x6e, (byte) 0x65, (byte) 0x2e, (byte) 0x74, (byte) 0x78,
(byte) 0x74, (byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5,
(byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x28, (byte) 0x1,
(byte) 0x0, (byte) 0x0, (byte) 0xbd, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Create Jar files used by the tests.
* The {@code byte} array {@code VALID_ZIP_WITH_NO_COMMENTS_BYTES} is written
* to disk to create the jar file: {@code Valid-CEN-Comment-Length.jar}.
*
* The jar file {@code InValid-CEN-Comment-Length.jar} is created by copying
* the {@code byte} array {@code VALID_ZIP_WITH_NO_COMMENTS_BYTES} and modifying
* the CEN file header comment length entry for "META-INF/MANIFEST.MF" so that
* new comment length will forward the CEN to a subsequent CEN file header
* entry.
*
* For {@code InValid-CEN-Comment-Length.jar}, the comment length is changed
* from {@code 0x0} to the {@code 0x37}.
*
* @throws IOException If an error occurs
*/
@BeforeAll
public static void setup() throws IOException {
Files.deleteIfExists(VALID_CEN_COMMENT_LENGTH_JAR);
Files.deleteIfExists(INVALID_CEN_COMMENT_LENGTH_JAR);
// Create the valid jar
Files.write(VALID_CEN_COMMENT_LENGTH_JAR, VALID_ZIP_WITH_NO_COMMENTS_BYTES);
// Now create an invalid jar
byte[] invalid_bytes = Arrays.copyOf(VALID_ZIP_WITH_NO_COMMENTS_BYTES,
VALID_ZIP_WITH_NO_COMMENTS_BYTES.length);
// Change CEN file Header comment length so that the length will
// result in the offset pointing to a subsequent CEN file header
// resulting in an invalid comment
invalid_bytes[536] = 55;
Files.write(INVALID_CEN_COMMENT_LENGTH_JAR, invalid_bytes);
}
/**
* Clean up after the test run
*
* @throws IOException If an error occurs
*/
@AfterAll
public static void cleanup() throws IOException {
Files.deleteIfExists(VALID_CEN_COMMENT_LENGTH_JAR);
Files.deleteIfExists(INVALID_CEN_COMMENT_LENGTH_JAR);
}
/**
* Validate that the original(valid) Jar file can be opened by {@code ZipFile}
* and the expected Zip entry can be found
* @throws IOException If an error occurs
*/
@Test
public void ZipFileValidCommentLengthTest() throws IOException {
try (ZipFile jf = new ZipFile(VALID_CEN_COMMENT_LENGTH_JAR.toFile())) {
ZipEntry ze = jf.getEntry(META_INF_MANIFEST_MF);
assertNotNull(ze);
assertEquals(META_INF_MANIFEST_MF, ze.getName());
}
}
/**
* Validate that the original(valid) Jar file can be opened by {@code JarFile}
* and the expected Zip entry can be found
* @throws IOException If an error occurs
*/
@Test
public void JarFileValidCommentLengthTest() throws IOException {
try (JarFile jf = new JarFile(VALID_CEN_COMMENT_LENGTH_JAR.toFile())) {
ZipEntry ze = jf.getEntry(META_INF_MANIFEST_MF);
assertNotNull(ze);
assertEquals(META_INF_MANIFEST_MF, ze.getName());
}
}
/**
* Validate that a ZipException is thrown when the CEN file header comment
* length is non-zero and the CEN entry does not contain a comment when
* the Jar file is opened by {@code ZipFile}
*/
@Test
public void ZipFileInValidCommentLengthTest() {
var ex= assertThrows(ZipException.class,
() -> new ZipFile(INVALID_CEN_COMMENT_LENGTH_JAR.toFile()));
assertEquals(INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT, ex.getMessage());
}
/**
* Validate that a ZipException is thrown when the CEN file header comment
* length is non-zero and the CEN entry does not contain a comment when
* the Jar file is opened by {@code JarFile}
*/
@Test
public void JarFileInValidCommentLengthTest() {
var ex= assertThrows(ZipException.class,
() -> new JarFile(INVALID_CEN_COMMENT_LENGTH_JAR.toFile()));
assertEquals(INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT, ex.getMessage());
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HexFormat;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.junit.jupiter.api.Test;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*
* @test
* @bug 8358456
* @summary verify that ZipFile.getInputStream(ZipFile) doesn't throw an unspecified exception
* for invalid compressed size of an entry
* @run junit InvalidCompressedSizeTest
*/
class InvalidCompressedSizeTest {
private static final String ENTRY_NAME = "foo-bar";
private static final byte[] ENTRY_CONTENT = new byte[]{0x42, 0x42};
// created through a call to createZIPContent()
private static final String ZIP_CONTENT_HEX = """
504b03041400080808005053c35a00000000000000000000000007000000666f6f2d6261727
3720200504b0708c41f441b0400000002000000504b010214001400080808005053c35ac41f
441b0400000002000000070000000000000000000000000000000000666f6f2d626172504b0
506000000000100010035000000390000000000
""";
// 0039 CENTRAL HEADER #1 02014B50
// ...
// 0043 Compression Method 0008 'Deflated'
// ...
// 004D Compressed Length 00000004
// 0051 Uncompressed Length 00000002
// ...
// 0067 Filename 'foo-bar'
// this is the offset in the ZIP content stream for the compressed size field
// for the entry of interest
private static final int COMP_SIZE_OFFSET = 0x004D;
// intentionally unused but left here to allow for constructing newer/updated
// ZIP_CONTENT_HEX, when necessary
private static String createZIPContent() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (final ZipOutputStream zos = new ZipOutputStream(baos)) {
final ZipEntry ze = new ZipEntry(ENTRY_NAME);
zos.putNextEntry(ze);
zos.write(ENTRY_CONTENT);
zos.closeEntry();
}
return HexFormat.of().formatHex(baos.toByteArray());
}
/*
* Calls ZipFile.getInputStream(ZipEntry) on a ZIP entry whose compressed size is
* intentionally set to 0. The test then verifies that the call to getInputStream()
* doesn't throw an unspecified exception.
*/
@Test
void testInvalidCompressedSize() throws Exception {
final byte[] originalZIPContent = HexFormat.of().parseHex(ZIP_CONTENT_HEX.replace("\n", ""));
final ByteBuffer zipContent = ByteBuffer.wrap(originalZIPContent).order(LITTLE_ENDIAN);
// overwrite the compressed size value in the entry's CEN to an invalid value of 0
zipContent.position(COMP_SIZE_OFFSET);
final int invalidCompressedSize = 0;
zipContent.putInt(invalidCompressedSize);
zipContent.rewind();
// write out the ZIP content so that it can be read through ZipFile
final Path zip = Files.createTempFile(Path.of("."), "8358456-", ".zip");
Files.write(zip, zipContent.array());
System.out.println("created ZIP " + zip + " with an invalid compressed size for entry");
try (final ZipFile zf = new ZipFile(zip.toFile())) {
final ZipEntry entry = zf.getEntry(ENTRY_NAME);
assertNotNull(entry, "missing entry " + ENTRY_NAME + " in ZIP file " + zip);
// verify that we are indeed testing a ZIP file with an invalid
// compressed size for the entry
assertEquals(0, entry.getCompressedSize(), "unexpected compressed size");
// merely open and close the InputStream to exercise the code which
// would incorrectly raise an exception. we don't read the contents
// of the stream because we have (intentionally) corrupted the metadata
// of the ZIP and that will cause the reading to fail.
try (final InputStream is = zf.getInputStream(entry)) {
System.out.println("successfully opened input stream " + is
+ " for entry " + entry.getName());
}
}
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.*;
import java.nio.*;
import java.util.*;
import java.util.zip.*;
public class LargeZipFile {
// If true, don't delete large ZIP file created for test.
static final boolean debug = System.getProperty("debug") != null;
static final int DATA_LEN = 1024 * 1024;
static final int DATA_SIZE = 8;
static long fileSize = 3L * 1024L * 1024L * 1024L; // 3GB
static boolean userFile = false;
static byte[] data;
static File largeFile;
static String lastEntryName;
/* args can be empty, in which case check a 3 GB file which is created for
* this test (and then deleted). Or it can be a number, in which case
* that designates the size of the file that's created for this test (and
* then deleted). Or it can be the name of a file to use for the test, in
* which case it is *not* deleted. Note that in this last case, the data
* comparison might fail.
*/
static void realMain (String[] args) throws Throwable {
if (args.length > 0) {
try {
fileSize = Long.parseLong(args[0]);
System.out.println("Testing with file of size " + fileSize);
} catch (NumberFormatException ex) {
largeFile = new File(args[0]);
if (!largeFile.exists()) {
throw new Exception("Specified file " + args[0] + " does not exist");
}
userFile = true;
System.out.println("Testing with user-provided file " + largeFile);
}
}
File testDir = null;
if (largeFile == null) {
testDir = new File(System.getProperty("test.scratch", "."),
"LargeZip");
if (testDir.exists()) {
if (!testDir.delete()) {
throw new Exception("Cannot delete already-existing test directory");
}
}
check(!testDir.exists() && testDir.mkdirs());
largeFile = new File(testDir, "largezip.zip");
createLargeZip();
}
readLargeZip();
if (!userFile && !debug) {
check(largeFile.delete());
check(testDir.delete());
}
}
static void createLargeZip() throws Throwable {
int iterations = DATA_LEN / DATA_SIZE;
ByteBuffer bb = ByteBuffer.allocate(DATA_SIZE);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < iterations; i++) {
bb.putDouble(0, Math.random());
baos.write(bb.array(), 0, DATA_SIZE);
}
data = baos.toByteArray();
try (FileOutputStream fos = new FileOutputStream(largeFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos))
{
long length = 0;
while (length < fileSize) {
ZipEntry ze = new ZipEntry("entry-" + length);
lastEntryName = ze.getName();
zos.putNextEntry(ze);
zos.write(data, 0, data.length);
zos.closeEntry();
length = largeFile.length();
}
System.out.println("Last entry written is " + lastEntryName);
}
}
static void readLargeZip() throws Throwable {
try (ZipFile zipFile = new ZipFile(largeFile)) {
ZipEntry entry = null;
String entryName = null;
int count = 0;
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
entry = entries.nextElement();
entryName = entry.getName();
count++;
}
System.out.println("Number of entries read: " + count);
System.out.println("Last entry read is " + entryName);
check(!entry.isDirectory());
if (check(entryName.equals(lastEntryName))) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = zipFile.getInputStream(entry);
byte buf[] = new byte[4096];
int len;
while ((len = is.read(buf)) >= 0) {
baos.write(buf, 0, len);
}
baos.close();
is.close();
check(Arrays.equals(data, baos.toByteArray()));
}
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void pass(String msg) {System.out.println(msg); passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void unexpected(Throwable t, String msg) {
System.out.println(msg); failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4770745 4828461
* @summary open zip files with more than 64k entries
* @run main/timeout=600 ManyEntries
* @author Martin Buchholz
*/
import java.util.*;
import java.util.zip.*;
import java.io.*;
public class ManyEntries {
public static void main(String[] args) throws Exception {
boolean runMoreTests = args.length > 0;
int[] sizes = (runMoreTests ?
new int[]{1, 2, 42, 65534, 65535, 65536, 65537, 68000} :
new int[]{1, 2, 42, 68000});
int[] methods = {ZipEntry.STORED, ZipEntry.DEFLATED};
for (int size : sizes)
for (int method : methods)
test(size, method);
}
// JDK doesn't like having zip file contents changed at runtime
static int uniquifier = 42;
static void test(int N, int method) throws Exception {
System.out.println("N="+N+", method="+method);
long time = System.currentTimeMillis() / 2000 * 2000;
CRC32 crc32 = new CRC32();
File zipFile = new File(++uniquifier + ".zip");
try {
zipFile.delete();
try (FileOutputStream fos = new FileOutputStream(zipFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos))
{
for (int i = 0; i < N; i++) {
ZipEntry e = new ZipEntry("DIR/"+i);
e.setMethod(method);
e.setTime(time);
if (method == ZipEntry.STORED) {
e.setSize(1);
crc32.reset();
crc32.update((byte)i);
e.setCrc(crc32.getValue());
} else {
e.setSize(0);
e.setCrc(0);
}
zos.putNextEntry(e);
zos.write(i);
}
}
try (ZipFile zip = new ZipFile(zipFile)) {
if (! (zip.size() == N))
throw new Exception("Bad ZipFile size: " + zip.size());
Enumeration entries = zip.entries();
for (int i = 0; i < N; i++) {
if (i % 1000 == 0) {System.gc(); System.runFinalization();}
if (! (entries.hasMoreElements()))
throw new Exception("only " + i + " elements");
ZipEntry e = (ZipEntry)entries.nextElement();
if (! (e.getSize() == 1))
throw new Exception("bad size: " + e.getSize());
if (! (e.getName().equals("DIR/" + i)))
throw new Exception("bad name: " + i);
if (! (e.getMethod() == method))
throw new Exception("getMethod="+e.getMethod()+", method=" + method);
if (! (e.getTime() == time))
throw new Exception("getTime="+e.getTime()+", time=" + time);
if (! (zip.getInputStream(e).read() == (i & 0xff)))
throw new Exception("Bad file contents: " + i);
}
if (entries.hasMoreElements())
throw new Exception("too many elements");
}
} finally {
zipFile.delete();
}
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 6423026
* @summary Check that it is possible to open more than 2,048 zip files on
* Windows.
*/
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class ManyZipFiles {
static final int numFiles = 3000;
public static void realMain(String[] args) throws Throwable {
// Linux does not yet allow opening this many files; Solaris
// 8 requires an explicit allocation of more file descriptors
// to succeed. Since this test is written to check for a
// Windows capability it is much simpler to only run it
// on that platform.
String osName = System.getProperty("os.name");
if (!(osName.startsWith("Windows"))) {
return;
}
// Create some zip data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
ZipEntry ze = new ZipEntry("test");
zos.putNextEntry(ze);
byte[] hello = "hello, world".getBytes("ASCII");
zos.write(hello, 0, hello.length);
zos.closeEntry();
zos.finish();
}
byte[] data = baos.toByteArray();
ZipFile zips[] = new ZipFile[numFiles];
try {
// Create a directory for zip files created below
File tmpdir = new File(
System.getProperty("java.io.tmpdir")
+ File.separator + "ManyZipFiles");
if (tmpdir.exists() && !tmpdir.isDirectory()) {
fail(tmpdir.getAbsolutePath()
+ " already exists but is not a directory");
return;
}
if (!tmpdir.exists()) {
if (!tmpdir.mkdirs()) {
fail("Couldn't create directory "
+ tmpdir.getAbsolutePath() + " for test files");
return;
}
} else if (!tmpdir.canWrite()) {
fail("Don't have write access for directory "
+ tmpdir.getAbsolutePath() + " for test files");
return;
}
tmpdir.deleteOnExit();
// Create and then open a large number of zip files
for (int i = 0; i < numFiles; i++) {
File f = File.createTempFile("test", ".zip", tmpdir);
f.deleteOnExit();
try (FileOutputStream fos = new FileOutputStream(f)) {
fos.write(data, 0, data.length);
}
try {
zips[i] = new ZipFile(f);
} catch (Throwable t) {
fail("Failed to open zip file #" + i + " named "
+ zips[i].getName());
throw t;
}
}
} finally {
// This finally block is due to bug 4171239. On Windows, if the
// file is still open at the end of the VM, deleteOnExit won't
// take place. "new ZipFile(...)" opens the zip file, so we have
// to explicitly close those opened above. This finally block can
// be removed when 4171239 is fixed. See also 6357433, against which
// 4171239 was closed as a duplicate.
for (int i = 0; i < numFiles; i++) {
if (zips[i] != null) {
try {
zips[i].close();
} catch (Throwable t) {
fail("At zip[" + i + "] named " + zips[i].getName()
+ " caught " + t);
}
}
}
}
pass();
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,816 @@
/*
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HexFormat;
import java.util.Map;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import static org.junit.jupiter.api.Assertions.*;
/* @test
* @bug 8314891
* @summary Validate that a ZipException is thrown when the extra len is 0
* and the CEN size, csize,LOC offset fields are set to 0xFFFFFFFF, the disk
* starting number is set to 0xFFFF or when we have a valid Zip64 Extra header
* size but missing the expected header fields.
* @run junit MissingZIP64EntriesTest
*/
public class MissingZIP64EntriesTest {
/*
* Byte array representing a ZIP file which contains a
* Zip64 Extra Header with only the size field.
* ----------------#1--------------------
* [Central Directory Header]
* 0x4d: Signature : 0x02014b50
* 0x51: Created Zip Spec : 0x2d [4.5]
* 0x52: Created OS : 0x0 [MS-DOS]
* 0x53: VerMadeby : 0x2d [0, 4.5]
* 0x54: VerExtract : 0x2d [4.5]
* 0x55: Flag : 0x808
* 0x57: Method : 0x8 [DEFLATED]
* 0x59: Last Mod Time : 0x57116922 [Thu Aug 17 13:09:04 EDT 2023]
* 0x5d: CRC : 0x57de98d2
* 0x61: Compressed Size : 0x16
* 0x65: Uncompressed Size: 0xffffffff
* 0x69: Name Length : 0x9
* 0x6b: Extra Length : 0xc
* Extra data:[01, 00, 08, 00, 14, 00, 00, 00, 00, 00, 00, 00]
* [tag=0x0001, sz=8]
* ->ZIP64: size *0x14
* [data= 14 00 00 00 00 00 00 00 ]
* 0x6d: Comment Length : 0x0
* 0x6f: Disk Start : 0x0
* 0x71: Attrs : 0x0
* 0x73: AttrsEx : 0x0
* 0x77: Loc Header Offset: 0x0
* 0x7b: File Name : Hello.txt
*/
public static byte[] ZIP_WITH_ZIP64_EXTRAHDR_SIZE_ONLY_BYTEARRAY = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x22, (byte) 0x69, (byte) 0x11, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0xf3,
(byte) 0x48, (byte) 0xcd, (byte) 0xc9, (byte) 0xc9, (byte) 0x57, (byte) 0x8, (byte) 0x49, (byte) 0xcd,
(byte) 0xcb, (byte) 0xcb, (byte) 0x2c, (byte) 0x56, (byte) 0x8, (byte) 0xc8, (byte) 0x49, (byte) 0xac,
(byte) 0x4c, (byte) 0x2d, (byte) 0x2a, (byte) 0x6, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7,
(byte) 0x8, (byte) 0xd2, (byte) 0x98, (byte) 0xde, (byte) 0x57, (byte) 0x16, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1,
(byte) 0x2, (byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0x22, (byte) 0x69, (byte) 0x11, (byte) 0x57, (byte) 0xd2, (byte) 0x98, (byte) 0xde,
(byte) 0x57, (byte) 0x16, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0x9, (byte) 0x0, (byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f,
(byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x8, (byte) 0x0,
(byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x43, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x4d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Byte array representing a Zip file with no extra header fields
* ----------------#1--------------------
* [Central Directory Header]
* 0x2f: Signature : 0x02014b50
* 0x33: Created Zip Spec : 0x14 [2.0]
* 0x34: Created OS : 0x3 [UNIX]
* 0x35: VerMadeby : 0x314 [3, 2.0]
* 0x36: VerExtract : 0x14 [2.0]
* 0x37: Flag : 0x2
* 0x39: Method : 0x8 [DEFLATED]
* 0x3b: Last Mod Time : 0x57039c0d [Thu Aug 03 19:32:26 EDT 2023]
* 0x3f: CRC : 0x31963516
* 0x43: Compressed Size : 0x8
* 0x47: Uncompressed Size: 0x6
* 0x4b: Name Length : 0x9
* 0x4d: Extra Length : 0x0
* 0x4f: Comment Length : 0x0
* 0x51: Disk Start : 0x0
* 0x53: Attrs : 0x1
* 0x55: AttrsEx : 0x81a40000
* 0x59: Loc Header Offset: 0x0
* 0x5d: File Name : Hello.txt
*/
public static byte[] ZIP_WITH_NO_EXTRA_LEN_BYTEARRAY = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x2, (byte) 0x0,
(byte) 0x8, (byte) 0x0, (byte) 0xd, (byte) 0x9c, (byte) 0x3, (byte) 0x57, (byte) 0x16, (byte) 0x35,
(byte) 0x96, (byte) 0x31, (byte) 0x8, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x6, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0xf3,
(byte) 0x48, (byte) 0xcd, (byte) 0xc9, (byte) 0xc9, (byte) 0xe7, (byte) 0x2, (byte) 0x0, (byte) 0x50,
(byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, (byte) 0x3, (byte) 0x14, (byte) 0x0, (byte) 0x2,
(byte) 0x0, (byte) 0x8, (byte) 0x0, (byte) 0xd, (byte) 0x9c, (byte) 0x3, (byte) 0x57, (byte) 0x16,
(byte) 0x35, (byte) 0x96, (byte) 0x31, (byte) 0x8, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x6,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa4,
(byte) 0x81, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65, (byte) 0x6c,
(byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x50, (byte) 0x4b,
(byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x37, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2f, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Byte array representing a ZIP file which contains a
* Zip64 Extra Header with only the LOC offset field.
* ----------------#1--------------------
* [Central Directory Header]
* 0x4d: Signature : 0x02014b50
* 0x51: Created Zip Spec : 0x2d [4.5]
* 0x52: Created OS : 0x0 [MS-DOS]
* 0x53: VerMadeby : 0x2d [0, 4.5]
* 0x54: VerExtract : 0x2d [4.5]
* 0x55: Flag : 0x808
* 0x57: Method : 0x8 [DEFLATED]
* 0x59: Last Mod Time : 0x572d69c5 [Wed Sep 13 13:14:10 EDT 2023]
* 0x5d: CRC : 0x57de98d2
* 0x61: Compressed Size : 0x16
* 0x65: Uncompressed Size: 0x14
* 0x69: Name Length : 0x9
* 0x6b: Extra Length : 0xc
* Extra data:[01, 00, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00]
* [tag=0x0001, sz=8]
* ->ZIP64: LOC Off *0x0
* [data= 00 00 00 00 00 00 00 00 ]
* 0x6d: Comment Length : 0x0
* 0x6f: Disk Start : 0x0
* 0x71: Attrs : 0x0
* 0x73: AttrsEx : 0x0
* 0x77: Loc Header Offset: 0xffffffff
* 0x7b: File Name : Hello.txt
*/
public static byte[] ZIP_WITH_ZIP64_EXTRAHDR_LOC_ONLY_BYTEARRAY = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0xc5, (byte) 0x69, (byte) 0x2d, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0xf3,
(byte) 0x48, (byte) 0xcd, (byte) 0xc9, (byte) 0xc9, (byte) 0x57, (byte) 0x8, (byte) 0x49, (byte) 0xcd,
(byte) 0xcb, (byte) 0xcb, (byte) 0x2c, (byte) 0x56, (byte) 0x8, (byte) 0xc8, (byte) 0x49, (byte) 0xac,
(byte) 0x4c, (byte) 0x2d, (byte) 0x2a, (byte) 0x6, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7,
(byte) 0x8, (byte) 0xd2, (byte) 0x98, (byte) 0xde, (byte) 0x57, (byte) 0x16, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1,
(byte) 0x2, (byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0xc5, (byte) 0x69, (byte) 0x2d, (byte) 0x57, (byte) 0xd2, (byte) 0x98, (byte) 0xde,
(byte) 0x57, (byte) 0x16, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f,
(byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x8, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x43, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x4d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Byte array representing a ZIP file which contains a
* Zip64 Extra Header with only the compressed size field.
* ----------------#1--------------------
* [Central Directory Header]
* 0x4d: Signature : 0x02014b50
* 0x51: Created Zip Spec : 0x2d [4.5]
* 0x52: Created OS : 0x0 [MS-DOS]
* 0x53: VerMadeby : 0x2d [0, 4.5]
* 0x54: VerExtract : 0x2d [4.5]
* 0x55: Flag : 0x808
* 0x57: Method : 0x8 [DEFLATED]
* 0x59: Last Mod Time : 0x572d6960 [Wed Sep 13 13:11:00 EDT 2023]
* 0x5d: CRC : 0x57de98d2
* 0x61: Compressed Size : 0xffffffff
* 0x65: Uncompressed Size: 0x14
* 0x69: Name Length : 0x9
* 0x6b: Extra Length : 0xc
* Extra data:[01, 00, 08, 00, 16, 00, 00, 00, 00, 00, 00, 00]
* [tag=0x0001, sz=8]
* ->ZIP64: csize *0x16
* [data= 16 00 00 00 00 00 00 00 ]
* 0x6d: Comment Length : 0x0
* 0x6f: Disk Start : 0x0
* 0x71: Attrs : 0x0
* 0x73: AttrsEx : 0x0
* 0x77: Loc Header Offset: 0x0
* 0x7b: File Name : Hello.txt
*/
public static byte[] ZIP_WITH_ZIP64_EXTRAHDR_CSIZE_ONLY_BYTEARRAY = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x60, (byte) 0x69, (byte) 0x2d, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0xf3,
(byte) 0x48, (byte) 0xcd, (byte) 0xc9, (byte) 0xc9, (byte) 0x57, (byte) 0x8, (byte) 0x49, (byte) 0xcd,
(byte) 0xcb, (byte) 0xcb, (byte) 0x2c, (byte) 0x56, (byte) 0x8, (byte) 0xc8, (byte) 0x49, (byte) 0xac,
(byte) 0x4c, (byte) 0x2d, (byte) 0x2a, (byte) 0x6, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7,
(byte) 0x8, (byte) 0xd2, (byte) 0x98, (byte) 0xde, (byte) 0x57, (byte) 0x16, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1,
(byte) 0x2, (byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0x60, (byte) 0x69, (byte) 0x2d, (byte) 0x57, (byte) 0xd2, (byte) 0x98, (byte) 0xde,
(byte) 0x57, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x14, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0xc, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f,
(byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x8, (byte) 0x0,
(byte) 0x16, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x43, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x4d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Byte array representing a Zip file with a zero length ZIP64 Extra Header
* ----------------#1--------------------
* [Central Directory Header]
* 0x43: Signature : 0x02014b50
* 0x47: Created Zip Spec : 0x2d [4.5]
* 0x48: Created OS : 0x3 [UNIX]
* 0x49: VerMadeby : 0x32d [3, 4.5]
* 0x4a: VerExtract : 0x2d [4.5]
* 0x4b: Flag : 0x800
* 0x4d: Method : 0x8 [DEFLATED]
* 0x4f: Last Mod Time : 0x572c3477 [Tue Sep 12 06:35:46 EDT 2023]
* 0x53: CRC : 0x31963516
* 0x57: Compressed Size : 0x8
* 0x5b: Uncompressed Size: 0x6
* 0x5f: Name Length : 0x9
* 0x61: Extra Length : 0x4
* Extra data:[01, 00, 00, 00]
* [tag=0x0001, sz=0]
* ->ZIP64:
* 0x63: Comment Length : 0x0
* 0x65: Disk Start : 0x0
* 0x67: Attrs : 0x0
* 0x69: AttrsEx : 0x81a40000
* 0x6d: Loc Header Offset: 0x0
* 0x71: File Name : Hello.txt
*/
public static byte[] ZIP_WITH_ZEROLEN_ZIP64_EXTRAHDR_BYTEARRAY = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x77, (byte) 0x34, (byte) 0x2c, (byte) 0x57, (byte) 0x16, (byte) 0x35,
(byte) 0x96, (byte) 0x31, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0x9, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x1,
(byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xf3, (byte) 0x48, (byte) 0xcd, (byte) 0xc9, (byte) 0xc9,
(byte) 0xe7, (byte) 0x2, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d,
(byte) 0x3, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x77,
(byte) 0x34, (byte) 0x2c, (byte) 0x57, (byte) 0x16, (byte) 0x35, (byte) 0x96, (byte) 0x31, (byte) 0x8,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9,
(byte) 0x0, (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa4, (byte) 0x81, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74,
(byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b,
(byte) 0x6, (byte) 0x6, (byte) 0x2c, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3b, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x43, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b,
(byte) 0x6, (byte) 0x7, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x7e, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x3b, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x43, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Byte array representing a ZIP file which contains a
* Zip64 Extra Header with the size and csize fields.
* ----------------#1--------------------
* [Central Directory Header]
* 0x4d: Signature : 0x02014b50
* 0x51: Created Zip Spec : 0x2d [4.5]
* 0x52: Created OS : 0x0 [MS-DOS]
* 0x53: VerMadeby : 0x2d [0, 4.5]
* 0x54: VerExtract : 0x2d [4.5]
* 0x55: Flag : 0x808
* 0x57: Method : 0x8 [DEFLATED]
* 0x59: Last Mod Time : 0x572c6445 [Tue Sep 12 12:34:10 EDT 2023]
* 0x5d: CRC : 0x57de98d2
* 0x61: Compressed Size : 0xffffffff
* 0x65: Uncompressed Size: 0xffffffff
* 0x69: Name Length : 0x9
* 0x6b: Extra Length : 0x14
* Extra data:[01, 00, 10, 00, 14, 00, 00, 00, 00, 00, 00, 00, 16, 00, 00, 00, 00, 00, 00, 00]
* [tag=0x0001, sz=16]
* ->ZIP64: size *0x14 csize *0x16
* [data= 14 00 00 00 00 00 00 00 16 00 00 00 00 00 00 00 ]
* 0x6d: Comment Length : 0x0
* 0x6f: Disk Start : 0x0
* 0x71: Attrs : 0x0
* 0x73: AttrsEx : 0x0
* 0x77: Loc Header Offset: 0x0
* 0x7b: File Name : Hello.txt
*/
public static byte[] ZIP_WITH_TWO_ZIP64_HEADER_ENTRIES_BYTEARRAY = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x45, (byte) 0x64, (byte) 0x2c, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0xf3,
(byte) 0x48, (byte) 0xcd, (byte) 0xc9, (byte) 0xc9, (byte) 0x57, (byte) 0x8, (byte) 0x49, (byte) 0xcd,
(byte) 0xcb, (byte) 0xcb, (byte) 0x2c, (byte) 0x56, (byte) 0x8, (byte) 0xc8, (byte) 0x49, (byte) 0xac,
(byte) 0x4c, (byte) 0x2d, (byte) 0x2a, (byte) 0x6, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7,
(byte) 0x8, (byte) 0xd2, (byte) 0x98, (byte) 0xde, (byte) 0x57, (byte) 0x16, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1,
(byte) 0x2, (byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0x45, (byte) 0x64, (byte) 0x2c, (byte) 0x57, (byte) 0xd2, (byte) 0x98, (byte) 0xde,
(byte) 0x57, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0x9, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f,
(byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x10, (byte) 0x0,
(byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x16, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x4b, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x4d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Byte array representing a ZIP file which contains a
* Zip64 Extra Header with the size,csize, and LOC offset fields.
* ----------------#1--------------------
* [Central Directory Header]
* 0x4d: Signature : 0x02014b50
* 0x51: Created Zip Spec : 0x2d [4.5]
* 0x52: Created OS : 0x0 [MS-DOS]
* 0x53: VerMadeby : 0x2d [0, 4.5]
* 0x54: VerExtract : 0x2d [4.5]
* 0x55: Flag : 0x808
* 0x57: Method : 0x8 [DEFLATED]
* 0x59: Last Mod Time : 0x572d7214 [Wed Sep 13 14:16:40 EDT 2023]
* 0x5d: CRC : 0x57de98d2
* 0x61: Compressed Size : 0xffffffff
* 0x65: Uncompressed Size: 0xffffffff
* 0x69: Name Length : 0x9
* 0x6b: Extra Length : 0x1c
* Extra data:[01, 00, 18, 00, 14, 00, 00, 00, 00, 00, 00, 00, 16, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
* [tag=0x0001, sz=24]
* ->ZIP64: size *0x14 csize *0x16 LOC Off *0x0
* [data= 14 00 00 00 00 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]
* 0x6d: Comment Length : 0x0
* 0x6f: Disk Start : 0x0
* 0x71: Attrs : 0x0
* 0x73: AttrsEx : 0x0
* 0x77: Loc Header Offset: 0xffffffff
* 0x7b: File Name : Hello.txt
*/
public static byte[] ZIP_WITH_ZIP64_EXTRAHDR_ALL_BYTEARRAY = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x14, (byte) 0x72, (byte) 0x2d, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x9, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x48, (byte) 0x65,
(byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0xf3,
(byte) 0x48, (byte) 0xcd, (byte) 0xc9, (byte) 0xc9, (byte) 0x57, (byte) 0x8, (byte) 0x49, (byte) 0xcd,
(byte) 0xcb, (byte) 0xcb, (byte) 0x2c, (byte) 0x56, (byte) 0x8, (byte) 0xc8, (byte) 0x49, (byte) 0xac,
(byte) 0x4c, (byte) 0x2d, (byte) 0x2a, (byte) 0x6, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x7,
(byte) 0x8, (byte) 0xd2, (byte) 0x98, (byte) 0xde, (byte) 0x57, (byte) 0x16, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1,
(byte) 0x2, (byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0x14, (byte) 0x72, (byte) 0x2d, (byte) 0x57, (byte) 0xd2, (byte) 0x98, (byte) 0xde,
(byte) 0x57, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0x9, (byte) 0x0, (byte) 0x1c, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f,
(byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x18, (byte) 0x0,
(byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x16, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x53, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x4d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/**
* Enable debug output
*/
private static final boolean DEBUG = false;
/**
* Name of the Zip file that we create from the byte array
*/
public static final String ZIPFILE_NAME = "validZipFile.zip";
/**
* Name of the Zip file that we modify/corrupt
*/
public static final String BAD_ZIP_NAME = "zipWithInvalidZip64ExtraField.zip";
/**
* Zip file entry that will be accessed by some the tests
*/
private static final String ZIP_FILE_ENTRY_NAME = "Hello.txt";
/**
* Expected Error messages
*/
private static final String INVALID_EXTRA_LENGTH =
"Invalid CEN header (invalid zip64 extra len size)";
private static final String INVALID_ZIP64_EXTRAHDR_SIZE =
"Invalid CEN header (invalid zip64 extra data field size)";
/**
* Disk starting number offset for the Zip file created from the
* ZIP_WITH_NO_EXTRA_LEN_BYTEARRAY array
*/
private static final int DISKNO_OFFSET_ZIP_NO_EXTRA_LEN = 0x51;
/**
* Value to set the size, csize, or LOC offset CEN fields to when their
* actual value is stored in the Zip64 Extra Header
*/
private static final long ZIP64_MAGICVAL = 0xFFFFFFFFL;
/**
* Value to set the Disk Start number offset CEN field to when the
* actual value is stored in the Zip64 Extra Header
*/
private static final int ZIP64_MAGICCOUNT = 0xFFFF;
/**
* Copy of the byte array for the ZIP to be modified by a given test run
*/
private byte[] zipArrayCopy;
/**
* Little-endian ByteBuffer for manipulating the ZIP copy
*/
private ByteBuffer buffer;
/**
* The MethodSource returning a byte array representing the Zip file,
* CEN offsets to set to 0xFFFFFFFF and the expected
* ZipException error message when there are missing Zip64 Extra header fields
* @return Arguments used in each test run
*/
private static Stream<Arguments> InvalidZip64MagicValues() {
return Stream.of(
// Byte array representing the Zip file, compressed size offset,
// and expected ZipException Message
Arguments.of(ZIP_WITH_ZIP64_EXTRAHDR_SIZE_ONLY_BYTEARRAY,
0x61, INVALID_ZIP64_EXTRAHDR_SIZE),
// Byte array representing the Zip file, LOC offset and expected ZipException Message
Arguments.of(ZIP_WITH_ZIP64_EXTRAHDR_SIZE_ONLY_BYTEARRAY,
0x77, INVALID_ZIP64_EXTRAHDR_SIZE),
// Byte array representing the Zip file, LOC offset and expected ZipException Message
Arguments.of(ZIP_WITH_TWO_ZIP64_HEADER_ENTRIES_BYTEARRAY,
0x77, INVALID_ZIP64_EXTRAHDR_SIZE)
);
}
/**
* The MethodSource of CEN offsets to set to 0xFFFFFFFF or 0xFFFF when the Extra Length
* size is 0 for the Zip file created using ZIP_WITH_NO_EXTRA_LEN_BYTEARRAY
* @return Arguments used in each test run
*/
private static Stream<Arguments> MissingZip64ExtraFieldEntries() {
return Stream.of(
// Compressed size offset
Arguments.of(0x43),
// Size offset
Arguments.of(0x47),
// Disk start number offset
Arguments.of(DISKNO_OFFSET_ZIP_NO_EXTRA_LEN),
// LOC offset
Arguments.of(0x59)
);
}
/**
* The MethodSource of CEN offsets to set to 0xFFFFFFFF when the ZIP64 extra header
* Length size is 0 for the Zip file created using
* ZIP_WITH_ZEROLEN_ZIP64_EXTRAHDR_BYTEARRAY
* @return Arguments used in each test run
*/
private static Stream<Arguments> zip64ZeroLenHeaderExtraFieldEntries() {
return Stream.of(
// Compressed size offset
Arguments.of(0x57),
// Size offset
Arguments.of(0x5b),
// LOC offset
Arguments.of(0x6d)
);
}
/**
* The MethodSource which will return a byte array representing a
* valid Zip file and the expected content for the Zip file entry 'Hello.txt'.
* @return Arguments used in each test run
*/
private static Stream<Arguments> validZipFiles() {
return Stream.of(
// Byte array representing the Zip file, and the expected entry content
Arguments.of(ZIP_WITH_ZIP64_EXTRAHDR_SIZE_ONLY_BYTEARRAY,
"Hello Tennis Players"),
Arguments.of(ZIP_WITH_TWO_ZIP64_HEADER_ENTRIES_BYTEARRAY,
"Hello Tennis Players"),
Arguments.of(ZIP_WITH_ZIP64_EXTRAHDR_LOC_ONLY_BYTEARRAY,
"Hello Tennis Players"),
Arguments.of(ZIP_WITH_ZIP64_EXTRAHDR_CSIZE_ONLY_BYTEARRAY,
"Hello Tennis Players"),
Arguments.of(ZIP_WITH_ZIP64_EXTRAHDR_ALL_BYTEARRAY,
"Hello Tennis Players"),
Arguments.of(ZIP_WITH_NO_EXTRA_LEN_BYTEARRAY,
"Hello\n")
);
}
/**
* Initial test setup
* @throws IOException if an error occurs
*/
@BeforeAll
public static void setup() throws IOException {
Files.deleteIfExists(Path.of(ZIPFILE_NAME));
Files.deleteIfExists(Path.of(BAD_ZIP_NAME));
}
/**
* Delete the Zip file that will be modified by each test
* @throws IOException if an error occurs
*/
@BeforeEach
public void beforeEachTestRun() throws IOException {
Files.deleteIfExists(Path.of(ZIPFILE_NAME));
Files.deleteIfExists(Path.of(BAD_ZIP_NAME));
}
/**
* Verify that a ZipException is thrown by ZipFile if the Zip64 header
* does not contain the required field
* @param zipArray Byte array representing the Zip file
* @param offset Offset of the CEN Header field to set to 0xFFFFFFFF
* @param errorMessage Expected ZipException error message
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("InvalidZip64MagicValues")
public void invalidZip64ExtraHeaderZipFileTest(byte[] zipArray, int offset,
String errorMessage) throws IOException {
// Set the CEN csize or LOC offset field to 0xFFFFFFFF. There will not
// be the expected Zip64 Extra Header field resulting in a ZipException
// being thrown
zipArrayCopy = zipArray.clone();
buffer = ByteBuffer.wrap(zipArrayCopy).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(offset, (int) ZIP64_MAGICVAL);
Files.write(Path.of(BAD_ZIP_NAME), zipArrayCopy);
ZipException ex = assertThrows(ZipException.class, () -> {
openWithZipFile(BAD_ZIP_NAME, ZIP_FILE_ENTRY_NAME, null);
});
assertTrue(ex.getMessage().equals(errorMessage),
"Unexpected ZipException message: " + ex.getMessage());
}
/**
* Verify that a ZipException is thrown by Zip FS if the Zip64 header
* does not contain the required field
* @param zipArray Byte array representing the Zip file
* @param offset Offset of the CEN Header field to set to 0xFFFFFFFF
* @param errorMessage Expected ZipException error message
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("InvalidZip64MagicValues")
public void invalidZip64ExtraHeaderZipFSTest(byte[] zipArray, int offset,
String errorMessage) throws IOException {
// Set the CEN csize or LOC offset field to 0xFFFFFFFF. There will not
// be the expected Zip64 Extra Header field resulting in a ZipException
// being thrown
zipArrayCopy = zipArray.clone();
buffer = ByteBuffer.wrap(zipArrayCopy).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(offset, (int)ZIP64_MAGICVAL);
Files.write(Path.of(BAD_ZIP_NAME), zipArrayCopy);
ZipException ex = assertThrows(ZipException.class, () -> {
openWithZipFS(BAD_ZIP_NAME, ZIP_FILE_ENTRY_NAME, null);
});
assertTrue(ex.getMessage().equals(errorMessage),
"Unexpected ZipException message: " + ex.getMessage());
}
/**
* Verify that ZipFile will throw a ZipException if the CEN
* Extra length is 0 and the CEN size, csize, LOC offset field is set to
* 0xFFFFFFFF or the disk starting number is set to 0xFFFF
* @param offset Offset of the CEN Header field to set to 0xFFFFFFFF or 0xFFFF
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("MissingZip64ExtraFieldEntries")
public void zipFileBadExtraLength(int offset) throws IOException {
zipArrayCopy = ZIP_WITH_NO_EXTRA_LEN_BYTEARRAY.clone();
buffer = ByteBuffer.wrap(zipArrayCopy).order(ByteOrder.LITTLE_ENDIAN);
if (offset == DISKNO_OFFSET_ZIP_NO_EXTRA_LEN) {
buffer.putShort(offset, (short) ZIP64_MAGICCOUNT);
} else {
buffer.putInt(offset, (int) ZIP64_MAGICVAL);
}
Files.write(Path.of(BAD_ZIP_NAME), zipArrayCopy);
ZipException ex = assertThrows(ZipException.class, () -> {
openWithZipFile(BAD_ZIP_NAME, ZIP_FILE_ENTRY_NAME, null);
});
assertTrue(ex.getMessage().equals(INVALID_EXTRA_LENGTH),
"Unexpected ZipException message: " + ex.getMessage());
}
/**
* Verify that ZipFS will throw a ZipException if the CEN
* Extra length is 0 and the CEN size, csize, LOC offset field is set to
* 0xFFFFFFFF or the disk starting number is set to 0xFFFF
* @param offset the offset of the CEN Header field to set to 0xFFFFFFFF or 0xFFFF
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("MissingZip64ExtraFieldEntries")
public void zipFSBadExtraLength(int offset) throws IOException {
zipArrayCopy = ZIP_WITH_NO_EXTRA_LEN_BYTEARRAY.clone();
buffer = ByteBuffer.wrap(zipArrayCopy).order(ByteOrder.LITTLE_ENDIAN);
if (offset == DISKNO_OFFSET_ZIP_NO_EXTRA_LEN) {
buffer.putShort(offset, (short) ZIP64_MAGICCOUNT);
} else {
buffer.putInt(offset, (int) ZIP64_MAGICVAL);
}
Files.write(Path.of(BAD_ZIP_NAME), zipArrayCopy);
ZipException ex = assertThrows(ZipException.class, () -> {
openWithZipFS(BAD_ZIP_NAME, ZIP_FILE_ENTRY_NAME, null);
});
assertTrue(ex.getMessage().equals(INVALID_EXTRA_LENGTH),
"Unexpected ZipException message: " + ex.getMessage());
}
/**
* Verify that ZipFile will throw a ZipException if the ZIP64 extra header
* has a size of 0 and the CEN size, csize, or the LOC offset field is set to
* 0xFFFFFFFF
* @param offset the offset of the CEN Header field to set to 0xFFFFFFFF
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("zip64ZeroLenHeaderExtraFieldEntries")
public void zipFileZeroLenExtraHeader(int offset) throws IOException {
zipArrayCopy = ZIP_WITH_ZEROLEN_ZIP64_EXTRAHDR_BYTEARRAY.clone();
buffer = ByteBuffer.wrap(zipArrayCopy).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(offset, (int) ZIP64_MAGICVAL);
Files.write(Path.of(BAD_ZIP_NAME), zipArrayCopy);
ZipException ex = assertThrows(ZipException.class, () -> {
openWithZipFile(BAD_ZIP_NAME, ZIP_FILE_ENTRY_NAME, null);
});
assertTrue(ex.getMessage().equals(INVALID_ZIP64_EXTRAHDR_SIZE),
"Unexpected ZipException message: " + ex.getMessage());
}
/**
* Verify that ZipFS will throw a ZipException if the ZIP64 extra header
* has a size of 0 and the CEN size, csize, or the LOC offset field is set to
* 0xFFFFFFFF
* @param offset the offset of the CEN Header field to set to 0xFFFFFFFF
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("zip64ZeroLenHeaderExtraFieldEntries")
public void zipFSZeroLenExtraHeader(int offset) throws IOException {
zipArrayCopy = ZIP_WITH_ZEROLEN_ZIP64_EXTRAHDR_BYTEARRAY.clone();
buffer = ByteBuffer.wrap(zipArrayCopy).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(offset, (int) ZIP64_MAGICVAL);
Files.write(Path.of(BAD_ZIP_NAME), zipArrayCopy);
ZipException ex = assertThrows(ZipException.class, () -> {
openWithZipFS(BAD_ZIP_NAME, ZIP_FILE_ENTRY_NAME, null);
});
assertTrue(ex.getMessage().equals(INVALID_ZIP64_EXTRAHDR_SIZE),
"Unexpected ZipException message: " + ex.getMessage());
}
/**
* Verify that ZipFile will read the Zip files created from the
* byte arrays prior to modifying the arrays to check that the
* expected ZipException is thrown.
* @param zipFile the byte array which represents the Zip file that should
* be opened and read successfully.
* @param message the expected text contained within the Zip entry
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("validZipFiles")
public void readValidZipFile(byte[] zipFile, String message) throws IOException {
// Write out the Zip file from the byte array
Files.write(Path.of(ZIPFILE_NAME), zipFile);
openWithZipFile(ZIPFILE_NAME, ZIP_FILE_ENTRY_NAME, message);
}
/**
* Verify that ZipFS will read the Zip files created from the
* byte arrays prior to modifying the arrays to check that the
* expected ZipException is thrown.
* @param zipFile the byte array which represents the Zip file that should
* be opened and read successfully.
* @param message the expected text contained within the Zip entry
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("validZipFiles")
public void readValidZipFileWithZipFs(byte[] zipFile, String message)
throws IOException {
// Write out the Zip file from the byte array
Files.write(Path.of(ZIPFILE_NAME), zipFile);
openWithZipFS(ZIPFILE_NAME, ZIP_FILE_ENTRY_NAME, message);
}
/**
* Utility method used to open a Zip file using ZipFile by the tests.
* @param zipFile name of the Zip file to open
* @param entryName Zip entry to read when the Zip file is expected to be
* able to be opened
* @param entryContents the expected contents for the Zip entry
* @throws IOException if an error occurs
*/
private static void openWithZipFile(String zipFile, String entryName,
String entryContents) throws IOException {
try (ZipFile zf = new ZipFile(zipFile)) {
ZipEntry ze = zf.getEntry(entryName);
try (InputStream is = zf.getInputStream(ze)) {
String result = new String(is.readAllBytes());
if (DEBUG) {
var hx = HexFormat.ofDelimiter(", ").withPrefix("0x");
System.out.printf("Error: Zip File read :%s%n[%s]%n", result,
hx.formatHex(result.getBytes()));
}
// entryContents will be null when an exception is expected
if (entryContents != null) {
assertEquals(entryContents, result);
}
}
}
}
/**
* Utility method used to open a Zip file using ZipFS by the tests.
* @param zipFile name of the Zip file to open
* @param entryName Zip entry to read when the Zip file is expected to be
* able to be opened
* @param entryContents the expected contents for the Zip entry
* @throws IOException if an error occurs
*/
private static void openWithZipFS(String zipFile, String entryName,
String entryContents) throws IOException {
try (FileSystem fs = FileSystems.newFileSystem(
Path.of(zipFile), Map.of())) {
Path p = fs.getPath(entryName);
String result = new String(Files.readAllBytes(p));
if (DEBUG) {
var hx = HexFormat.ofDelimiter(", ").withPrefix("0x");
System.out.printf("Error: Zip FS read :%s%n[%s]%n", result,
hx.formatHex(result.getBytes()));
}
// entryContents will be null when an exception is expected
if (entryContents != null) {
assertEquals(entryContents, result);
}
}
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8038491
* @summary Crash in ZipFile.read() when ZipFileInputStream is shared between threads
* @library /test/lib
* @build jdk.test.lib.Platform
* jdk.test.lib.util.FileUtils
* @run main MultiThreadedReadTest
*/
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import jdk.test.lib.util.FileUtils;
public class MultiThreadedReadTest extends Thread {
private static final int NUM_THREADS = 10;
private static final String ZIPFILE_NAME =
System.currentTimeMillis() + "-bug8038491-tmp.large.zip";
private static final String ZIPENTRY_NAME = "random.txt";
private static InputStream is = null;
public static void main(String args[]) throws Exception {
createZipFile();
try (ZipFile zf = new ZipFile(new File(ZIPFILE_NAME))) {
is = zf.getInputStream(zf.getEntry(ZIPENTRY_NAME));
Thread[] threadArray = new Thread[NUM_THREADS];
for (int i = 0; i < threadArray.length; i++) {
threadArray[i] = new MultiThreadedReadTest();
}
for (int i = 0; i < threadArray.length; i++) {
threadArray[i].start();
}
for (int i = 0; i < threadArray.length; i++) {
threadArray[i].join();
}
} finally {
long t = System.currentTimeMillis();
FileUtils.deleteFileIfExistsWithRetry(Paths.get(ZIPFILE_NAME));
System.out.println("Deleting zip file took:" +
(System.currentTimeMillis() - t) + "ms");
}
}
private static void createZipFile() throws Exception {
CRC32 crc32 = new CRC32();
long t = System.currentTimeMillis();
File zipFile = new File(ZIPFILE_NAME);
try (FileOutputStream fos = new FileOutputStream(zipFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos)) {
ZipEntry e = new ZipEntry(ZIPENTRY_NAME);
e.setMethod(ZipEntry.STORED);
byte[] toWrite = "BLAH".repeat(10_000).getBytes();
e.setTime(t);
e.setSize(toWrite.length);
crc32.reset();
crc32.update(toWrite);
e.setCrc(crc32.getValue());
zos.putNextEntry(e);
zos.write(toWrite);
}
}
@Override
public void run() {
try {
while (is.read() != -1) { }
} catch (Exception e) {
System.out.println("read exception:" + e);
// Swallow any Exceptions (which are expected) - we're only interested in the crash
}
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 8340684
@summary Verify unspecified, but long-standing behavior when reading
from an input stream obtained using ZipFile::getInputStream after
the ZipFile has been closed.
@run junit ReadAfterClose
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ReadAfterClose {
// ZIP file used in this test
private Path zip = Path.of("read-after-close.zip");
/**
* Create a sample ZIP file for use by this test
* @throws IOException if an unexpected IOException occurs
*/
@BeforeEach
public void setUp() throws IOException {
byte[] content = "hello".repeat(1000).getBytes(StandardCharsets.UTF_8);
try (OutputStream out = Files.newOutputStream(zip);
ZipOutputStream zo = new ZipOutputStream(out)) {
{
zo.putNextEntry(new ZipEntry("deflated.txt"));
zo.write(content);
}
{
ZipEntry entry = new ZipEntry("stored.txt");
entry.setMethod(ZipEntry.STORED);
CRC32 crc = new CRC32();
crc.update(content);
entry.setCrc(crc.getValue());
entry.setSize(content.length);
zo.putNextEntry(entry);
zo.write(content);
}
}
}
/**
* Delete the ZIP file produced by this test
* @throws IOException if an unexpected IOException occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/**
* Produce arguments with a variation of stored / deflated entries,
* and read behavior before closing the ZipFile.
* @return
*/
public static Stream<Arguments> arguments() {
return Stream.of(
Arguments.of("stored.txt", true),
Arguments.of("stored.txt", false),
Arguments.of("deflated.txt", true),
Arguments.of("deflated.txt", false)
);
}
/**
* Attempting to read from an InputStream obtained by ZipFile.getInputStream
* after the backing ZipFile is closed should throw IOException
*
* @throws IOException if an unexpected IOException occurs
*/
@ParameterizedTest
@MethodSource("arguments")
public void readAfterClose(String entryName, boolean readFirst) throws IOException {
// Retain a reference to an input stream backed by a closed ZipFile
InputStream in;
try (ZipFile zf = new ZipFile(zip.toFile())) {
in = zf.getInputStream(new ZipEntry(entryName));
// Optionally consume a single byte from the stream before closing
if (readFirst) {
in.read();
}
}
assertThrows(IOException.class, () -> {
in.read();
});
}
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*
* @test
* @bug 6374379
* @summary Verify that we can read zip file names > 255 chars long
* @run junit ReadLongZipFileName
*/
public class ReadLongZipFileName {
private static final String ENTRY_NAME = "testFile.txt";
private static final String LONG_DIR_NAME = "abcdefghijklmnopqrstuvwx"; // 24 chars
private static final String JAR_FILE_NAME = "areallylargejarfilename.jar"; // 27 chars
/*
* Creates a jar file at a path whose path name length and jar file name length
* combined is large. Then use the java.util.jar.JarFile APIs to open and read the jar file
* to verify the APIs work against those jar/zip files.
*/
@Test
public void testOpenAndReadJarFile() throws Exception {
int minRequiredPathLength = 600; // long enough to definitely fail.
Path tmpDir = Files.createTempDirectory(Path.of("."), "ReadLongZipFileName-test")
.normalize().toAbsolutePath();
// Create a directory structure long enough that the filename will
// put us over the minRequiredLength.
int currentPathLength = 0;
Path jarFileDir = tmpDir;
do {
jarFileDir = jarFileDir.resolve(LONG_DIR_NAME);
Files.createDirectories(jarFileDir);
currentPathLength = jarFileDir.toFile().getCanonicalPath().length();
} while (currentPathLength < (minRequiredPathLength - JAR_FILE_NAME.length()));
// Create a new Jar file: use jar instead of zip to make sure long
// names work for both zip and jar subclass.
Path jarFilePath = jarFileDir.resolve(JAR_FILE_NAME);
System.out.println("creating a jar file at " + jarFilePath);
try (JarOutputStream out = new JarOutputStream(
new BufferedOutputStream(Files.newOutputStream(jarFilePath)))) {
out.putNextEntry(new JarEntry(ENTRY_NAME));
out.write(1);
}
assertTrue(Files.isRegularFile(jarFilePath),
"jar file " + jarFilePath + " does not exist or is not a file");
try (JarFile readJarFile = new JarFile(jarFilePath.toFile())) {
JarEntry je = readJarFile.getJarEntry(ENTRY_NAME);
assertNotNull(je, "missing jar entry: " + ENTRY_NAME + " in jar file " + jarFilePath);
DataInputStream dis = new DataInputStream(readJarFile.getInputStream(je));
byte val = dis.readByte();
assertEquals(1, val, "unexpected byte " + val + " read from entry " + ENTRY_NAME);
try {
dis.readByte();
Assertions.fail("Read past expected EOF");
} catch (IOException e) {
// expected
System.out.println("received the expected exception: " + e);
}
}
System.out.println("Successfully opened and read contents from a jar file with a name "
+ jarFilePath.toFile().getCanonicalPath().length() + " characters long");
}
}

View file

@ -0,0 +1,934 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Formatter;
import java.util.Map;
import java.util.stream.Stream;
import java.util.zip.ZipFile;
/* @test
* @bug 8313765
* @summary Validate that a Zip File with an Extra Header with a data size
* of 0 can be read.
* @run junit ReadNonStandardExtraHeadersTest
*/
public class ReadNonStandardExtraHeadersTest {
/*
* Byte array holding a ZIP file which contains an
* Extra field header which has a data size of 0.
*
* ---------------#1--------------------
* [Central Directory Header]
* 0x664: Signature : 0x02014b50
* 0x668: Created Zip Spec : 0xa [1.0]
* 0x669: Created OS : 0x0 [MS-DOS]
* 0x66a: VerMadeby : 0xa [0, 1.0]
* 0x66b: VerExtract : 0xa [1.0]
* 0x66c: Flag : 0x800
* 0x66e: Method : 0x0 [STORED]
* 0x670: Last Mod Time : 0x385ca437 [Thu Feb 28 20:33:46 EST 2008]
* 0x674: CRC : 0x694c6952
* 0x678: Compressed Size : 0x624
* 0x67c: Uncompressed Size: 0x624
* 0x680: Name Length : 0x1b
* 0x682: Extra Length : 0x7
* ->[tag=cafe, size=0]
* 0x684: Comment Length : 0x0
* 0x686: Disk Start : 0x0
* 0x688: Attrs : 0x0
* 0x68a: AttrsEx : 0x0
* 0x68e: Loc Header Offset: 0x0
* 0x692: File Name : res/drawable/size_48x48.jpg
*
* [Local File Header]
* 0x0: Signature : 0x04034b50
* 0x4: Version : 0xa [1.0]
* 0x6: Flag : 0x800
* 0x8: Method : 0x0 [STORED]
* 0xa: LastMTime : 0x385ca437 [Thu Feb 28 20:33:46 EST 2008]
* 0xe: CRC : 0x694c6952
* 0x12: CSize : 0x624
* 0x16: Size : 0x624
* 0x1a: Name Length : 0x1b [res/drawable/size_48x48.jpg]
* 0x1c: ExtraLength : 0x7
* ->[tag=cafe, size=0]
* 0x1e: File Name : [res/drawable/size_48x48.jpg]
* [End Central Directory Header]
* Signature : 0x06054b50
* ENDCEN Off : 0x6b4
* Disk Entries: 0x1
* Total Entries: 0x1
* CEN Size : 0x50
* Offset CEN : 0x664
* Comment Len : 0x0 []
*/
public static byte[] VALID_APK_FILE = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0xa, (byte) 0x0, (byte) 0x0, (byte) 0x8,
(byte) 0x0, (byte) 0x0, (byte) 0x37, (byte) 0xa4, (byte) 0x5c, (byte) 0x38, (byte) 0x52, (byte) 0x69,
(byte) 0x4c, (byte) 0x69, (byte) 0x24, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x24, (byte) 0x6,
(byte) 0x0, (byte) 0x0, (byte) 0x1b, (byte) 0x0, (byte) 0x7, (byte) 0x0, (byte) 0x72, (byte) 0x65,
(byte) 0x73, (byte) 0x2f, (byte) 0x64, (byte) 0x72, (byte) 0x61, (byte) 0x77, (byte) 0x61, (byte) 0x62,
(byte) 0x6c, (byte) 0x65, (byte) 0x2f, (byte) 0x73, (byte) 0x69, (byte) 0x7a, (byte) 0x65, (byte) 0x5f,
(byte) 0x34, (byte) 0x38, (byte) 0x78, (byte) 0x34, (byte) 0x38, (byte) 0x2e, (byte) 0x6a, (byte) 0x70,
(byte) 0x67, (byte) 0xfe, (byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0xff, (byte) 0xd8, (byte) 0xff, (byte) 0xe0, (byte) 0x0, (byte) 0x10, (byte) 0x4a, (byte) 0x46,
(byte) 0x49, (byte) 0x46, (byte) 0x0, (byte) 0x1, (byte) 0x1, (byte) 0x1, (byte) 0x0, (byte) 0x48,
(byte) 0x0, (byte) 0x48, (byte) 0x0, (byte) 0x0, (byte) 0xff, (byte) 0xfe, (byte) 0x0, (byte) 0x16,
(byte) 0x28, (byte) 0x63, (byte) 0x29, (byte) 0x20, (byte) 0x32, (byte) 0x30, (byte) 0x30, (byte) 0x37,
(byte) 0x20, (byte) 0x50, (byte) 0x68, (byte) 0x69, (byte) 0x6c, (byte) 0x20, (byte) 0x44, (byte) 0x75,
(byte) 0x62, (byte) 0x61, (byte) 0x63, (byte) 0x68, (byte) 0xff, (byte) 0xdb, (byte) 0x0, (byte) 0x43,
(byte) 0x0, (byte) 0x6, (byte) 0x4, (byte) 0x5, (byte) 0x6, (byte) 0x5, (byte) 0x4, (byte) 0x6,
(byte) 0x6, (byte) 0x5, (byte) 0x6, (byte) 0x7, (byte) 0x7, (byte) 0x6, (byte) 0x8, (byte) 0xa,
(byte) 0x10, (byte) 0xa, (byte) 0xa, (byte) 0x9, (byte) 0x9, (byte) 0xa, (byte) 0x14, (byte) 0xe,
(byte) 0xf, (byte) 0xc, (byte) 0x10, (byte) 0x17, (byte) 0x14, (byte) 0x18, (byte) 0x18, (byte) 0x17,
(byte) 0x14, (byte) 0x16, (byte) 0x16, (byte) 0x1a, (byte) 0x1d, (byte) 0x25, (byte) 0x1f, (byte) 0x1a,
(byte) 0x1b, (byte) 0x23, (byte) 0x1c, (byte) 0x16, (byte) 0x16, (byte) 0x20, (byte) 0x2c, (byte) 0x20,
(byte) 0x23, (byte) 0x26, (byte) 0x27, (byte) 0x29, (byte) 0x2a, (byte) 0x29, (byte) 0x19, (byte) 0x1f,
(byte) 0x2d, (byte) 0x30, (byte) 0x2d, (byte) 0x28, (byte) 0x30, (byte) 0x25, (byte) 0x28, (byte) 0x29,
(byte) 0x28, (byte) 0xff, (byte) 0xdb, (byte) 0x0, (byte) 0x43, (byte) 0x1, (byte) 0x7, (byte) 0x7,
(byte) 0x7, (byte) 0xa, (byte) 0x8, (byte) 0xa, (byte) 0x13, (byte) 0xa, (byte) 0xa, (byte) 0x13,
(byte) 0x28, (byte) 0x1a, (byte) 0x16, (byte) 0x1a, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28,
(byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28,
(byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28,
(byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28,
(byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28,
(byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28,
(byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0x28, (byte) 0xff, (byte) 0xc0,
(byte) 0x0, (byte) 0x11, (byte) 0x8, (byte) 0x0, (byte) 0x30, (byte) 0x0, (byte) 0x30, (byte) 0x3,
(byte) 0x1, (byte) 0x11, (byte) 0x0, (byte) 0x2, (byte) 0x11, (byte) 0x1, (byte) 0x3, (byte) 0x11,
(byte) 0x1, (byte) 0xff, (byte) 0xc4, (byte) 0x0, (byte) 0x1b, (byte) 0x0, (byte) 0x0, (byte) 0x2,
(byte) 0x2, (byte) 0x3, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x4, (byte) 0x5,
(byte) 0x6, (byte) 0x7, (byte) 0x1, (byte) 0x3, (byte) 0x8, (byte) 0x2, (byte) 0xff, (byte) 0xc4,
(byte) 0x0, (byte) 0x2e, (byte) 0x10, (byte) 0x0, (byte) 0x2, (byte) 0x2, (byte) 0x1, (byte) 0x2,
(byte) 0x4, (byte) 0x4, (byte) 0x5, (byte) 0x4, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x2, (byte) 0x3, (byte) 0x4, (byte) 0x11,
(byte) 0x0, (byte) 0x5, (byte) 0x6, (byte) 0x12, (byte) 0x21, (byte) 0x31, (byte) 0x41, (byte) 0x51,
(byte) 0x61, (byte) 0x71, (byte) 0x7, (byte) 0x13, (byte) 0x22, (byte) 0x42, (byte) 0x91, (byte) 0x33,
(byte) 0x62, (byte) 0x81, (byte) 0xa1, (byte) 0x52, (byte) 0xd1, (byte) 0xf0, (byte) 0xff, (byte) 0xc4,
(byte) 0x0, (byte) 0x1b, (byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x5, (byte) 0x1, (byte) 0x1,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x6, (byte) 0x2, (byte) 0x3, (byte) 0x4, (byte) 0x5,
(byte) 0x7, (byte) 0x1, (byte) 0x0, (byte) 0xff, (byte) 0xc4, (byte) 0x0, (byte) 0x33, (byte) 0x11,
(byte) 0x0, (byte) 0x1, (byte) 0x3, (byte) 0x2, (byte) 0x4, (byte) 0x4, (byte) 0x4, (byte) 0x4,
(byte) 0x5, (byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x2, (byte) 0x3, (byte) 0x4, (byte) 0x11, (byte) 0x5, (byte) 0x12,
(byte) 0x21, (byte) 0x31, (byte) 0x13, (byte) 0x51, (byte) 0x61, (byte) 0x71, (byte) 0x6, (byte) 0x32,
(byte) 0x41, (byte) 0x81, (byte) 0x91, (byte) 0xa1, (byte) 0xb1, (byte) 0xc1, (byte) 0x7, (byte) 0x14,
(byte) 0x42, (byte) 0xd1, (byte) 0xf0, (byte) 0x22, (byte) 0x33, (byte) 0x62, (byte) 0xa2, (byte) 0xf1,
(byte) 0xff, (byte) 0xda, (byte) 0x0, (byte) 0xc, (byte) 0x3, (byte) 0x1, (byte) 0x0, (byte) 0x2,
(byte) 0x11, (byte) 0x3, (byte) 0x11, (byte) 0x0, (byte) 0x3f, (byte) 0x0, (byte) 0xb4, (byte) 0x11,
(byte) 0xf4, (byte) 0x4c, (byte) 0xa, (byte) 0x12, (byte) 0x7b, (byte) 0x16, (byte) 0x2e, (byte) 0x5d,
(byte) 0xaf, (byte) 0x46, (byte) 0xac, (byte) 0x96, (byte) 0x6e, (byte) 0xcf, (byte) 0x1c, (byte) 0x10,
(byte) 0x46, (byte) 0x32, (byte) 0xd2, (byte) 0x48, (byte) 0xd8, (byte) 0x3, (byte) 0x48, (byte) 0x96,
(byte) 0x46, (byte) 0x44, (byte) 0xd2, (byte) 0xf7, (byte) 0x9b, (byte) 0x0, (byte) 0x91, (byte) 0x5,
(byte) 0x24, (byte) 0xd5, (byte) 0x52, (byte) 0x8, (byte) 0x60, (byte) 0x69, (byte) 0x73, (byte) 0x8e,
(byte) 0xc0, (byte) 0x6f, (byte) 0xfc, (byte) 0xfa, (byte) 0x28, (byte) 0x26, (byte) 0xe5, (byte) 0xf1,
(byte) 0x5f, (byte) 0x6b, (byte) 0x82, (byte) 0x52, (byte) 0x9b, (byte) 0x7d, (byte) 0x3b, (byte) 0x17,
(byte) 0x0, (byte) 0x38, (byte) 0xe7, (byte) 0x62, (byte) 0x22, (byte) 0x53, (byte) 0xea, (byte) 0x32,
(byte) 0x9, (byte) 0xfc, (byte) 0x81, (byte) 0xaa, (byte) 0x19, (byte) 0xfc, (byte) 0x43, (byte) 0x13,
(byte) 0xd, (byte) 0xa3, (byte) 0x69, (byte) 0x77, (byte) 0xcb, (byte) 0xf7, (byte) 0x47, (byte) 0xb4,
(byte) 0x1f, (byte) 0x86, (byte) 0x95, (byte) 0xd3, (byte) 0xb7, (byte) 0x35, (byte) 0x4c, (byte) 0xad,
(byte) 0x8f, (byte) 0xa5, (byte) 0xb3, (byte) 0x1f, (byte) 0x7d, (byte) 0x40, (byte) 0xf8, (byte) 0x12,
(byte) 0x8a, (byte) 0xd9, (byte) 0x7e, (byte) 0x26, (byte) 0x6d, (byte) 0xb7, (byte) 0x98, (byte) 0x8b,
(byte) 0x95, (byte) 0x2c, (byte) 0xd3, (byte) 0x3, (byte) 0xbc, (byte) 0x9f, (byte) 0xaa, (byte) 0x8b,
(byte) 0xee, (byte) 0x40, (byte) 0xc8, (byte) 0xfc, (byte) 0x6b, (byte) 0xb4, (byte) 0xf8, (byte) 0xfc,
(byte) 0x52, (byte) 0x7f, (byte) 0x71, (byte) 0xa5, (byte) 0xbd, (byte) 0x77, (byte) 0x1e, (byte) 0xff,
(byte) 0x0, (byte) 0xf1, (byte) 0x7b, (byte) 0x10, (byte) 0xfc, (byte) 0x34, (byte) 0xc4, (byte) 0x29,
(byte) 0x9b, (byte) 0x9e, (byte) 0x9a, (byte) 0x46, (byte) 0xcb, (byte) 0xd3, (byte) 0xca, (byte) 0xef,
(byte) 0x6b, (byte) 0x92, (byte) 0xf, (byte) 0xc4, (byte) 0x29, (byte) 0xd4, (byte) 0x13, (byte) 0x47,
(byte) 0x62, (byte) 0x14, (byte) 0x9a, (byte) 0x9, (byte) 0x12, (byte) 0x58, (byte) 0x9c, (byte) 0x73,
(byte) 0x2b, (byte) 0xa1, (byte) 0x4, (byte) 0x30, (byte) 0xf3, (byte) 0x7, (byte) 0x57, (byte) 0xac,
(byte) 0x7b, (byte) 0x5e, (byte) 0xd0, (byte) 0xe6, (byte) 0x9b, (byte) 0x82, (byte) 0x80, (byte) 0x24,
(byte) 0x82, (byte) 0x48, (byte) 0x1e, (byte) 0x62, (byte) 0x95, (byte) 0xa5, (byte) 0xae, (byte) 0x1a,
(byte) 0x10, (byte) 0x45, (byte) 0x88, (byte) 0x3d, (byte) 0x42, (byte) 0xf7, (byte) 0x83, (byte) 0xae,
(byte) 0xdd, (byte) 0x75, (byte) 0xa1, (byte) 0x2, (byte) 0xd2, (byte) 0x47, (byte) 0x1a, (byte) 0x33,
(byte) 0xbb, (byte) 0x5, (byte) 0x45, (byte) 0x5, (byte) 0x99, (byte) 0x89, (byte) 0xc0, (byte) 0x0,
(byte) 0x77, (byte) 0x27, (byte) 0x49, (byte) 0xce, (byte) 0x0, (byte) 0xb9, (byte) 0x53, (byte) 0xcc,
(byte) 0x2e, (byte) 0x71, (byte) 0xb0, (byte) 0x17, (byte) 0x25, (byte) 0x73, (byte) 0xff, (byte) 0x0,
(byte) 0x1d, (byte) 0x71, (byte) 0x54, (byte) 0xfc, (byte) 0x4f, (byte) 0xba, (byte) 0x30, (byte) 0x46,
(byte) 0x64, (byte) 0xdb, (byte) 0x21, (byte) 0x62, (byte) 0x2b, (byte) 0xc5, (byte) 0xdb, (byte) 0x3f,
(byte) 0xbd, (byte) 0x87, (byte) 0xf9, (byte) 0x1f, (byte) 0xe8, (byte) 0x74, (byte) 0xf3, (byte) 0xc8,
(byte) 0x46, (byte) 0x25, (byte) 0x5e, (byte) 0xea, (byte) 0xb9, (byte) 0x3f, (byte) 0xc4, (byte) 0x6c,
(byte) 0x3e, (byte) 0xeb, (byte) 0x6d, (byte) 0xf0, (byte) 0xbf, (byte) 0x87, (byte) 0xa3, (byte) 0xc2,
(byte) 0xa0, (byte) 0x5, (byte) 0xc2, (byte) 0xf2, (byte) 0xbb, (byte) 0xcc, (byte) 0x7e, (byte) 0xc3,
(byte) 0xa0, (byte) 0xf9, (byte) 0x9d, (byte) 0x79, (byte) 0x59, (byte) 0xa, (byte) 0x2e, (byte) 0x7,
(byte) 0x4d, (byte) 0x54, (byte) 0x92, (byte) 0x8c, (byte) 0xd8, (byte) 0xdb, (byte) 0x29, (byte) 0x1f,
(byte) 0xe, (byte) 0x55, (byte) 0xbc, (byte) 0x86, (byte) 0x69, (byte) 0xea, (byte) 0x30, (byte) 0xc,
(byte) 0x22, (byte) 0x62, (byte) 0x63, (byte) 0x75, (byte) 0x24, (byte) 0x48, (byte) 0x83, (byte) 0xbe,
(byte) 0x7d, (byte) 0x3b, (byte) 0x75, (byte) 0x3e, (byte) 0x24, (byte) 0xe, (byte) 0xe4, (byte) 0x69,
(byte) 0xe8, (byte) 0xc, (byte) 0x8c, (byte) 0xbb, (byte) 0xd8, (byte) 0xa2, (byte) 0x56, (byte) 0x54,
(byte) 0xd3, (byte) 0x87, (byte) 0x32, (byte) 0x9, (byte) 0x4e, (byte) 0xae, (byte) 0x3a, (byte) 0x5b,
(byte) 0xd3, (byte) 0xaa, (byte) 0x7f, (byte) 0xc0, (byte) 0x7c, (byte) 0x5e, (byte) 0xdb, (byte) 0x2d,
(byte) 0xd1, (byte) 0x5, (byte) 0xd9, (byte) 0x11, (byte) 0x36, (byte) 0xb9, (byte) 0x64, (byte) 0xc3,
(byte) 0x87, (byte) 0x6c, (byte) 0x8, (byte) 0x89, (byte) 0xfb, (byte) 0x86, (byte) 0x7b, (byte) 0xf,
(byte) 0x3d, (byte) 0x59, (byte) 0xe1, (byte) 0x38, (byte) 0x8b, (byte) 0xa0, (byte) 0x7e, (byte) 0x43,
(byte) 0xe5, (byte) 0x3e, (byte) 0x9c, (byte) 0xba, (byte) 0xa1, (byte) 0x6f, (byte) 0x1b, (byte) 0x78,
(byte) 0x6e, (byte) 0x9a, (byte) 0xbe, (byte) 0x98, (byte) 0xd5, (byte) 0x5c, (byte) 0x36, (byte) 0x66,
(byte) 0xd, (byte) 0x9, (byte) 0xb0, (byte) 0xcd, (byte) 0x6f, (byte) 0xd2, (byte) 0x6f, (byte) 0xfe,
(byte) 0xbc, (byte) 0x8f, (byte) 0x4b, (byte) 0xab, (byte) 0xa6, (byte) 0xad, (byte) 0x9a, (byte) 0xf7,
(byte) 0x2b, (byte) 0xa4, (byte) 0xf4, (byte) 0xe7, (byte) 0x86, (byte) 0xc4, (byte) 0xf, (byte) 0xd5,
(byte) 0x65, (byte) 0x85, (byte) 0xc3, (byte) 0xab, (byte) 0x7b, (byte) 0x11, (byte) 0xd0, (byte) 0xe8,
(byte) 0xc5, (byte) 0xaf, (byte) 0xe, (byte) 0x17, (byte) 0x69, (byte) 0xba, (byte) 0xc2, (byte) 0xf2,
(byte) 0x16, (byte) 0x9b, (byte) 0x38, (byte) 0x59, (byte) 0x40, (byte) 0x78, (byte) 0xd6, (byte) 0xf3,
(byte) 0xa7, (byte) 0x8, (byte) 0xee, (byte) 0xe6, (byte) 0x33, (byte) 0xf5, (byte) 0x1a, (byte) 0xec,
(byte) 0xbd, (byte) 0x3c, (byte) 0x8f, (byte) 0x43, (byte) 0xfd, (byte) 0x13, (byte) 0xaa, (byte) 0xea,
(byte) 0xd9, (byte) 0x4f, (byte) 0x1, (byte) 0xe0, (byte) 0x72, (byte) 0x45, (byte) 0x78, (byte) 0x4c,
(byte) 0x2d, (byte) 0xfc, (byte) 0xf4, (byte) 0x39, (byte) 0xb6, (byte) 0xcc, (byte) 0x3e, (byte) 0x5b,
(byte) 0x7c, (byte) 0xd5, (byte) 0xb, (byte) 0x59, (byte) 0xb3, (byte) 0xa0, (byte) 0xd7, (byte) 0x85,
(byte) 0xb5, (byte) 0x53, (byte) 0x3e, (byte) 0xe9, (byte) 0xe6, (byte) 0xc6, (byte) 0x6a, (byte) 0x35,
(byte) 0xe4, (byte) 0x5d, (byte) 0xc1, (byte) 0x9d, (byte) 0x6b, (byte) 0xb0, (byte) 0x21, (byte) 0x9a,
(byte) 0x3c, (byte) 0x64, (byte) 0x7a, (byte) 0x8c, (byte) 0xe9, (byte) 0xa6, (byte) 0xe5, (byte) 0x7,
(byte) 0xfa, (byte) 0xb6, (byte) 0x53, (byte) 0xe5, (byte) 0x74, (byte) 0x9c, (byte) 0x33, (byte) 0xc2,
(byte) 0xf3, (byte) 0x75, (byte) 0x5b, (byte) 0xa5, (byte) 0xdf, (byte) 0x61, (byte) 0xda, (byte) 0x4f,
(byte) 0xc9, (byte) 0xb7, (byte) 0x3d, (byte) 0xc6, (byte) 0x4b, (byte) 0x32, (byte) 0x34, (byte) 0xb,
(byte) 0x65, (byte) 0x47, (byte) 0x53, (byte) 0x0, (byte) 0xc1, (byte) 0x18, (byte) 0xcf, (byte) 0x7f,
(byte) 0xbb, (byte) 0xa7, (byte) 0x99, (byte) 0xf5, (byte) 0x3a, (byte) 0x71, (byte) 0xb1, (byte) 0x49,
(byte) 0x35, (byte) 0xf2, (byte) 0x1d, (byte) 0x96, (byte) 0x6f, (byte) 0x8d, (byte) 0x62, (byte) 0x2f,
(byte) 0xa2, (byte) 0xc5, (byte) 0x44, (byte) 0xf1, (byte) 0x58, (byte) 0x90, (byte) 0xdb, (byte) 0x5b,
(byte) 0x70, (byte) 0xe, (byte) 0xbb, (byte) 0xf5, (byte) 0x6, (byte) 0xc5, (byte) 0x19, (byte) 0xc5,
(byte) 0xd4, (byte) 0xf6, (byte) 0xad, (byte) 0xbe, (byte) 0x18, (byte) 0x2d, (byte) 0x52, (byte) 0x13,
(byte) 0xd9, (byte) 0x2e, (byte) 0x3, (byte) 0x27, (byte) 0x2c, (byte) 0x79, (byte) 0x3c, (byte) 0x84,
(byte) 0x2, (byte) 0xf, (byte) 0x29, (byte) 0x39, (byte) 0xd, (byte) 0xea, (byte) 0x7d, (byte) 0x3b,
(byte) 0x75, (byte) 0xcb, (byte) 0x70, (byte) 0xe7, (byte) 0x75, (byte) 0xe2, (byte) 0x6, (byte) 0xdc,
(byte) 0xfd, (byte) 0x2, (byte) 0x8d, (byte) 0x84, (byte) 0xe2, (byte) 0x74, (byte) 0xfc, (byte) 0x73,
(byte) 0x55, (byte) 0x5d, (byte) 0x1b, (byte) 0xa6, (byte) 0x94, (byte) 0xed, (byte) 0xb1, (byte) 0xb7,
(byte) 0x60, (byte) 0x4f, (byte) 0xc3, (byte) 0x4d, (byte) 0x3d, (byte) 0x2, (byte) 0xb9, (byte) 0xf8,
(byte) 0x3, (byte) 0x88, (byte) 0xdb, (byte) 0x88, (byte) 0xf6, (byte) 0x48, (byte) 0x67, (byte) 0x6d,
(byte) 0xaf, (byte) 0x72, (byte) 0xa5, (byte) 0xcb, (byte) 0x1a, (byte) 0xab, (byte) 0x49, (byte) 0x6e,
(byte) 0x25, (byte) 0x45, (byte) 0x99, (byte) 0x80, (byte) 0xc3, (byte) 0x14, (byte) 0xc3, (byte) 0x12,
(byte) 0x46, (byte) 0x41, (byte) 0xee, (byte) 0x6, (byte) 0x8f, (byte) 0x68, (byte) 0xe7, (byte) 0x32,
(byte) 0xc4, (byte) 0xd2, (byte) 0x79, (byte) 0x6f, (byte) 0xe8, (byte) 0x4a, (byte) 0x2, (byte) 0xc5,
(byte) 0x29, (byte) 0x44, (byte) 0x15, (byte) 0x52, (byte) 0x34, (byte) 0x2, (byte) 0xd1, (byte) 0x72,
(byte) 0x43, (byte) 0x48, (byte) 0xb1, (byte) 0x0, (byte) 0xea, (byte) 0x1, (byte) 0x1d, (byte) 0x88,
(byte) 0x49, (byte) 0xae, (byte) 0xc0, (byte) 0x96, (byte) 0xea, (byte) 0x4f, (byte) 0x5a, (byte) 0x6f,
(byte) 0xd2, (byte) 0x9a, (byte) 0x36, (byte) 0x8d, (byte) 0xbd, (byte) 0x88, (byte) 0xc1, (byte) 0xd4,
(byte) 0x77, (byte) 0x8c, (byte) 0xcd, (byte) 0x2d, (byte) 0x3e, (byte) 0xaa, (byte) 0xce, (byte) 0x37,
(byte) 0x18, (byte) 0x9e, (byte) 0x24, (byte) 0x6e, (byte) 0xe0, (byte) 0xdf, (byte) 0xe0, (byte) 0xb9,
(byte) 0xfa, (byte) 0x4a, (byte) 0xf3, (byte) 0x50, (byte) 0xbd, (byte) 0x3d, (byte) 0x4b, (byte) 0x0,
(byte) 0xac, (byte) 0xd0, (byte) 0xb9, (byte) 0x46, (byte) 0x1e, (byte) 0xa3, (byte) 0xc7, (byte) 0xdb,
(byte) 0x42, (byte) 0xd3, (byte) 0x30, (byte) 0xb4, (byte) 0x90, (byte) 0x56, (byte) 0xaf, (byte) 0x86,
(byte) 0xd4, (byte) 0x89, (byte) 0x58, (byte) 0x1e, (byte) 0xd3, (byte) 0xa1, (byte) 0x4c, (byte) 0x76,
(byte) 0xe1, (byte) 0x1b, (byte) 0xdc, (byte) 0x84, (byte) 0xd9, (byte) 0x23, (byte) 0xe4, (byte) 0x2b,
(byte) 0x7, (byte) 0x91, (byte) 0x73, (byte) 0x8e, (byte) 0x65, (byte) 0x1d, (byte) 0x48, (byte) 0xf7,
(byte) 0x20, (byte) 0x11, (byte) 0xfc, (byte) 0xea, (byte) 0x31, (byte) 0xd1, (byte) 0x59, (byte) 0x55,
(byte) 0x36, (byte) 0x69, (byte) 0x20, (byte) 0x73, (byte) 0x62, (byte) 0x75, (byte) 0x9c, (byte) 0x74,
(byte) 0x1d, (byte) 0x2f, (byte) 0xa5, (byte) 0xfd, (byte) 0xb7, (byte) 0xf6, (byte) 0x52, (byte) 0x7b,
(byte) 0xd3, (byte) 0x52, (byte) 0xde, (byte) 0x52, (byte) 0x2b, (byte) 0x97, (byte) 0xab, (byte) 0x6,
(byte) 0x44, (byte) 0x95, (byte) 0x5a, (byte) 0x34, (byte) 0xce, (byte) 0x3e, (byte) 0x5a, (byte) 0x82,
(byte) 0xa0, (byte) 0x9f, (byte) 0xe, (byte) 0xfd, (byte) 0x7d, (byte) 0x87, (byte) 0xe7, (byte) 0x4c,
(byte) 0xc, (byte) 0xf1, (byte) 0x13, (byte) 0xc2, (byte) 0x36, (byte) 0xb8, (byte) 0x59, (byte) 0xdc,
(byte) 0xf8, (byte) 0x23, (byte) 0xdd, (byte) 0x59, (byte) 0x25, (byte) 0x2d, (byte) 0x35, (byte) 0xdd,
(byte) 0xc3, (byte) 0x0, (byte) 0x9e, (byte) 0xa7, (byte) 0x7b, (byte) 0x77, (byte) 0x20, (byte) 0xe9,
(byte) 0xd9, (byte) 0x1, (byte) 0x7e, (byte) 0xed, (byte) 0xcd, (byte) 0xd4, (byte) 0xc5, (byte) 0x16,
(byte) 0xcc, (byte) 0xcb, (byte) 0x60, (byte) 0xca, (byte) 0xe1, (byte) 0xd, (byte) 0x76, (byte) 0x24,
(byte) 0x16, (byte) 0x70, (byte) 0x70, (byte) 0x1, (byte) 0xfd, (byte) 0xd8, (byte) 0xc7, (byte) 0x5f,
(byte) 0x51, (byte) 0x9e, (byte) 0xd8, (byte) 0xd3, (byte) 0xd1, (byte) 0x42, (byte) 0x1, (byte) 0x11,
(byte) 0xbe, (byte) 0xf7, (byte) 0x3c, (byte) 0xb9, (byte) 0xfd, (byte) 0xd5, (byte) 0x9e, (byte) 0xd,
(byte) 0x1d, (byte) 0x4c, (byte) 0x14, (byte) 0xe7, (byte) 0x10, (byte) 0xa0, (byte) 0x94, (byte) 0x59,
(byte) 0xb7, (byte) 0xce, (byte) 0xc7, (byte) 0xed, (byte) 0xa6, (byte) 0xfa, (byte) 0xfa, (byte) 0x5c,
(byte) 0x6b, (byte) 0xe9, (byte) 0xd4, (byte) 0x9b, (byte) 0x2b, (byte) 0xe3, (byte) 0x82, (byte) 0x23,
(byte) 0x9a, (byte) 0xaf, (byte) 0xc, (byte) 0xd1, (byte) 0xa9, (byte) 0x6a, (byte) 0x8c, (byte) 0xd4,
(byte) 0x6c, (byte) 0x57, (byte) 0x4f, (byte) 0x97, (byte) 0x24, (byte) 0x32, (byte) 0xb2, (byte) 0x3f,
(byte) 0xd5, (byte) 0xdc, (byte) 0xb0, (byte) 0x64, (byte) 0x24, (byte) 0x15, (byte) 0x24, (byte) 0x92,
(byte) 0xe, (byte) 0x7f, (byte) 0x81, (byte) 0xa3, (byte) 0x8a, (byte) 0x48, (byte) 0xf8, (byte) 0x10,
(byte) 0xb6, (byte) 0x2e, (byte) 0x4b, (byte) 0x3c, (byte) 0xc5, (byte) 0xaa, (byte) 0xce, (byte) 0x21,
(byte) 0x5b, (byte) 0x25, (byte) 0x5b, (byte) 0xbf, (byte) 0x59, (byte) 0xbf, (byte) 0x61, (byte) 0xb0,
(byte) 0x1e, (byte) 0xc0, (byte) 0x4, (byte) 0xbe, (byte) 0x6a, (byte) 0xf9, (byte) 0xce, (byte) 0x35,
(byte) 0xe7, (byte) 0x35, (byte) 0x4b, (byte) 0x50, (byte) 0xe, (byte) 0x3c, (byte) 0xe1, (byte) 0x64,
(byte) 0xdc, (byte) 0xe5, (byte) 0x4b, (byte) 0x35, (byte) 0xf1, (byte) 0x15, (byte) 0xe0, (byte) 0x30,
(byte) 0x5b, (byte) 0xc2, (byte) 0x41, (byte) 0xe0, (byte) 0x1b, (byte) 0xfd, (byte) 0xff, (byte) 0x0,
(byte) 0xc2, (byte) 0xae, (byte) 0xae, (byte) 0x97, (byte) 0x89, (byte) 0xa8, (byte) 0xdd, (byte) 0x5d,
(byte) 0x61, (byte) 0x38, (byte) 0xb3, (byte) 0xa8, (byte) 0x8e, (byte) 0x57, (byte) 0x6a, (byte) 0xcf,
(byte) 0xa7, (byte) 0x6f, (byte) 0xd9, (byte) 0x40, (byte) 0xdf, (byte) 0x68, (byte) 0xdc, (byte) 0xab,
(byte) 0x37, (byte) 0x24, (byte) 0xd4, (byte) 0xa6, (byte) 0x38, (byte) 0xf1, (byte) 0x45, (byte) 0xe7,
(byte) 0x1f, (byte) 0x91, (byte) 0xaa, (byte) 0x39, (byte) 0x29, (byte) 0x65, (byte) 0x6e, (byte) 0xed,
(byte) 0x5a, (byte) 0x15, (byte) 0x1e, (byte) 0x37, (byte) 0x47, (byte) 0x30, (byte) 0xd2, (byte) 0x40,
(byte) 0x3b, (byte) 0x9b, (byte) 0x7d, (byte) 0x53, (byte) 0x3d, (byte) 0xaf, (byte) 0x67, (byte) 0xdd,
(byte) 0xf7, (byte) 0x14, (byte) 0x58, (byte) 0xaa, (byte) 0xd1, (byte) 0x9d, (byte) 0xd5, (byte) 0x49,
(byte) 0xc1, (byte) 0x65, (byte) 0xe5, (byte) 0x51, (byte) 0x9c, (byte) 0x7d, (byte) 0xc7, (byte) 0x3,
(byte) 0xc3, (byte) 0x4d, (byte) 0xb2, (byte) 0x92, (byte) 0x69, (byte) 0x8d, (byte) 0x98, (byte) 0xd2,
(byte) 0xa4, (byte) 0x49, (byte) 0x89, (byte) 0x61, (byte) 0x94, (byte) 0xf, (byte) 0x7d, (byte) 0x44,
(byte) 0x92, (byte) 0x0, (byte) 0xe7, (byte) 0xda, (byte) 0xfa, (byte) 0xdc, (byte) 0x9b, (byte) 0x6d,
(byte) 0xa0, (byte) 0xb9, (byte) 0x56, (byte) 0x47, (byte) 0x5, (byte) 0xf0, (byte) 0x2c, (byte) 0x5b,
(byte) 0x4d, (byte) 0x94, (byte) 0xdc, (byte) 0x37, (byte) 0x7, (byte) 0xf9, (byte) 0x97, (byte) 0x81,
(byte) 0xe6, (byte) 0x54, (byte) 0x89, (byte) 0x8a, (byte) 0xa2, (byte) 0x1f, (byte) 0x33, (byte) 0x8c,
(byte) 0x73, (byte) 0x1f, (byte) 0x7e, (byte) 0x9d, (byte) 0x4f, (byte) 0x7d, (byte) 0x5f, (byte) 0xd1,
(byte) 0x61, (byte) 0xdc, (byte) 0x1b, (byte) 0x3e, (byte) 0x53, (byte) 0x72, (byte) 0x36, (byte) 0x1c,
(byte) 0xbf, (byte) 0x9f, (byte) 0xe, (byte) 0xeb, (byte) 0x3a, (byte) 0xc7, (byte) 0xf1, (byte) 0xf8,
(byte) 0xab, (byte) 0x9e, (byte) 0xf6, (byte) 0xd1, (byte) 0x47, (byte) 0x90, (byte) 0x3b, (byte) 0xcc,
(byte) 0xed, (byte) 0x8b, (byte) 0xfb, (byte) 0x81, (byte) 0xa5, (byte) 0xbb, (byte) 0xdc, (byte) 0xf6,
(byte) 0xd4, (byte) 0x2b, (byte) 0x1e, (byte) 0x26, (byte) 0xe6, (byte) 0x41, (byte) 0x9e, (byte) 0xfa,
(byte) 0xb8, (byte) 0xe, (byte) 0xb8, (byte) 0x41, (byte) 0xce, (byte) 0x65, (byte) 0x8a, (byte) 0x5,
(byte) 0xea, (byte) 0x83, (byte) 0xe1, (byte) 0xa7, (byte) 0x4b, (byte) 0x41, (byte) 0x56, (byte) 0x65,
(byte) 0x2d, (byte) 0xbb, (byte) 0xb7, (byte) 0x7, (byte) 0x90, (byte) 0x12, (byte) 0x3c, (byte) 0x34,
(byte) 0xc3, (byte) 0xa3, (byte) 0xd5, (byte) 0x26, (byte) 0xeb, (byte) 0x4c, (byte) 0x5b, (byte) 0x6c,
(byte) 0x68, (byte) 0x73, (byte) 0xca, (byte) 0x33, (byte) 0xa4, (byte) 0xf0, (byte) 0xc0, (byte) 0x49,
(byte) 0x25, (byte) 0x1f, (byte) 0x14, (byte) 0x41, (byte) 0x40, (byte) 0xd7, (byte) 0x53, (byte) 0x64,
(byte) 0x22, (byte) 0x54, (byte) 0x79, (byte) 0x69, (byte) 0x4, (byte) 0xaf, (byte) 0x6, (byte) 0xa2,
(byte) 0xe2, (byte) 0xfa, (byte) 0x53, (byte) 0xd7, (byte) 0x5d, (byte) 0x1a, (byte) 0x4, (byte) 0xd3,
(byte) 0x85, (byte) 0xca, (byte) 0xff, (byte) 0xd9, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2,
(byte) 0xa, (byte) 0x0, (byte) 0xa, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x0, (byte) 0x0,
(byte) 0x37, (byte) 0xa4, (byte) 0x5c, (byte) 0x38, (byte) 0x52, (byte) 0x69, (byte) 0x4c, (byte) 0x69,
(byte) 0x24, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x24, (byte) 0x6, (byte) 0x0, (byte) 0x0,
(byte) 0x1b, (byte) 0x0, (byte) 0x7, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x2f, (byte) 0x64, (byte) 0x72,
(byte) 0x61, (byte) 0x77, (byte) 0x61, (byte) 0x62, (byte) 0x6c, (byte) 0x65, (byte) 0x2f, (byte) 0x73,
(byte) 0x69, (byte) 0x7a, (byte) 0x65, (byte) 0x5f, (byte) 0x34, (byte) 0x38, (byte) 0x78, (byte) 0x34,
(byte) 0x38, (byte) 0x2e, (byte) 0x6a, (byte) 0x70, (byte) 0x67, (byte) 0xfe, (byte) 0xca, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0,
(byte) 0x50, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x64, (byte) 0x6, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0,
};
/*
* Jar file created by Apache Commons-Compress which creates
* a CEN entry with a Zip64 extra header entry with a size of 0
* ----------------#1--------------------
* [Central Directory Header]
* 0x52b: Signature : 0x02014b50
* 0x52f: Created Zip Spec : 0x2d [4.5]
* 0x530: Created OS : 0x3 [UNIX]
* 0x531: VerMadeby : 0x32d [3, 4.5]
* 0x532: VerExtract : 0x2d [4.5]
* 0x533: Flag : 0x800
* 0x535: Method : 0x0 [STORED]
* 0x537: Last Mod Time : 0x570169c0 [Tue Aug 01 13:14:00 EDT 2023]
* 0x53b: CRC : 0x0
* 0x53f: Compressed Size : 0x0
* 0x543: Uncompressed Size: 0x0
* 0x547: Name Length : 0x9
* 0x549: Extra Length : 0x8
* [tag=0x0001, sz=0, data= ]
* ->ZIP64:
* [tag=0xcafe, sz=0, data= ]
* ->[tag=cafe, size=0]
* 0x54b: Comment Length : 0x0
* 0x54d: Disk Start : 0x0
* 0x54f: Attrs : 0x0
* 0x551: AttrsEx : 0x41ed0010
* 0x555: Loc Header Offset: 0x0
* 0x559: File Name : META-INF/
*/
public static byte[] COMMONS_COMPRESS_JAR = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8,
(byte) 0x0, (byte) 0x0, (byte) 0xc0, (byte) 0x69, (byte) 0x1, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0x9, (byte) 0x0, (byte) 0x18, (byte) 0x0, (byte) 0x4d, (byte) 0x45,
(byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0x1,
(byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xfe, (byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x50,
(byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0xbd, (byte) 0x69, (byte) 0x1, (byte) 0x57, (byte) 0x71, (byte) 0xa7, (byte) 0x16,
(byte) 0x53, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0x14, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x4d, (byte) 0x45, (byte) 0x54,
(byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0x4d, (byte) 0x41,
(byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45, (byte) 0x53, (byte) 0x54, (byte) 0x2e, (byte) 0x4d,
(byte) 0x46, (byte) 0x1, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x68, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5b, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xf3, (byte) 0x4d, (byte) 0xcc,
(byte) 0xcb, (byte) 0x4c, (byte) 0x4b, (byte) 0x2d, (byte) 0x2e, (byte) 0xd1, (byte) 0xd, (byte) 0x4b,
(byte) 0x2d, (byte) 0x2a, (byte) 0xce, (byte) 0xcc, (byte) 0xcf, (byte) 0xb3, (byte) 0x52, (byte) 0x30,
(byte) 0xd4, (byte) 0x33, (byte) 0xe0, (byte) 0xe5, (byte) 0x72, (byte) 0xcc, (byte) 0x43, (byte) 0x12,
(byte) 0x71, (byte) 0x2c, (byte) 0x48, (byte) 0x4c, (byte) 0xce, (byte) 0x48, (byte) 0x55, (byte) 0x0,
(byte) 0x8a, (byte) 0x1, (byte) 0x25, (byte) 0xd, (byte) 0xd, (byte) 0xf4, (byte) 0x2c, (byte) 0x78,
(byte) 0xb9, (byte) 0x9c, (byte) 0x8b, (byte) 0x52, (byte) 0x13, (byte) 0x4b, (byte) 0x52, (byte) 0x53,
(byte) 0x74, (byte) 0x9d, (byte) 0x2a, (byte) 0x81, (byte) 0x1a, (byte) 0x2c, (byte) 0xf4, (byte) 0xc,
(byte) 0xf4, (byte) 0x80, (byte) 0x12, (byte) 0xda, (byte) 0x46, (byte) 0xba, (byte) 0x66, (byte) 0xa,
(byte) 0x1a, (byte) 0xfe, (byte) 0x45, (byte) 0x89, (byte) 0xc9, (byte) 0x39, (byte) 0xa9, (byte) 0xa,
(byte) 0xce, (byte) 0xf9, (byte) 0x45, (byte) 0x5, (byte) 0xf9, (byte) 0x45, (byte) 0x89, (byte) 0x25,
(byte) 0x40, (byte) 0x3, (byte) 0x34, (byte) 0x79, (byte) 0xb9, (byte) 0x78, (byte) 0xb9, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x61, (byte) 0x69, (byte) 0x1, (byte) 0x57, (byte) 0x16, (byte) 0x64,
(byte) 0x9c, (byte) 0xc5, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0x19, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x5a, (byte) 0x69,
(byte) 0x70, (byte) 0x46, (byte) 0x69, (byte) 0x6c, (byte) 0x65, (byte) 0x50, (byte) 0x72, (byte) 0x6f,
(byte) 0x70, (byte) 0x65, (byte) 0x72, (byte) 0x74, (byte) 0x79, (byte) 0x54, (byte) 0x65, (byte) 0x73,
(byte) 0x74, (byte) 0x2e, (byte) 0x63, (byte) 0x6c, (byte) 0x61, (byte) 0x73, (byte) 0x73, (byte) 0x1,
(byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0xf, (byte) 0x7, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x85, (byte) 0x55, (byte) 0xcf, (byte) 0x53, (byte) 0xdb,
(byte) 0x46, (byte) 0x14, (byte) 0xfe, (byte) 0xd6, (byte) 0x3f, (byte) 0x90, (byte) 0x2d, (byte) 0x8b,
(byte) 0x38, (byte) 0x36, (byte) 0x21, (byte) 0xc1, (byte) 0x34, (byte) 0x4, (byte) 0x12, (byte) 0x70,
(byte) 0x63, (byte) 0xb0, (byte) 0x8d, (byte) 0xdb, (byte) 0x42, (byte) 0xd3, (byte) 0x36, (byte) 0xa6,
(byte) 0xf9, (byte) 0x45, (byte) 0x42, (byte) 0xb, (byte) 0x35, (byte) 0xe0, (byte) 0x62, (byte) 0x87,
(byte) 0xd4, (byte) 0xa4, (byte) 0x6d, (byte) 0x22, (byte) 0xdb, (byte) 0x4a, (byte) 0x22, (byte) 0x2a,
(byte) 0x4b, (byte) 0x1a, (byte) 0x49, (byte) 0x2e, (byte) 0x49, (byte) 0x67, (byte) 0x7a, (byte) 0xe8,
(byte) 0x4c, (byte) 0xcf, (byte) 0x9d, (byte) 0x69, (byte) 0xfe, (byte) 0x82, (byte) 0xde, (byte) 0x38,
(byte) 0xf5, (byte) 0x40, (byte) 0xe, (byte) 0x86, (byte) 0x94, (byte) 0x49, (byte) 0x73, (byte) 0xcb,
(byte) 0x21, (byte) 0xff, (byte) 0x51, (byte) 0x2f, (byte) 0xa5, (byte) 0x6f, (byte) 0x25, (byte) 0xc0,
(byte) 0x86, (byte) 0x98, (byte) 0x29, (byte) 0xcc, (byte) 0x68, (byte) 0x77, (byte) 0xdf, (byte) 0xee,
(byte) 0xf7, (byte) 0xbd, (byte) 0xf7, (byte) 0xbe, (byte) 0xf7, (byte) 0x76, (byte) 0xfd, (byte) 0xf6,
(byte) 0xdf, (byte) 0xbf, (byte) 0xfe, (byte) 0x6, (byte) 0x70, (byte) 0xb, (byte) 0xf, (byte) 0x45,
(byte) 0xf8, (byte) 0xe0, (byte) 0x17, (byte) 0x10, (byte) 0x90, (byte) 0x10, (byte) 0x44, (byte) 0xf,
(byte) 0xc3, (byte) 0xe9, (byte) 0x75, (byte) 0xf9, (byte) 0x47, (byte) 0x39, (byte) 0xa7, (byte) 0xc9,
(byte) 0xfa, (byte) 0xe3, (byte) 0xdc, (byte) 0x72, (byte) 0x75, (byte) 0x5d, (byte) 0xa9, (byte) 0x39,
(byte) 0xc, (byte) 0x3d, (byte) 0x33, (byte) 0xaa, (byte) 0xae, (byte) 0x3a, (byte) 0xd7, (byte) 0x18,
(byte) 0xfc, (byte) 0xa9, (byte) 0xf1, (byte) 0x55, (byte) 0x1, (byte) 0x21, (byte) 0x86, (byte) 0xbe,
(byte) 0x35, (byte) 0xd5, (byte) 0x9c, (byte) 0x53, (byte) 0x35, (byte) 0xa5, (byte) 0x68, (byte) 0x19,
(byte) 0xa6, (byte) 0x62, (byte) 0x39, (byte) 0xcf, (byte) 0xca, (byte) 0x8a, (byte) 0xed, (byte) 0x88,
(byte) 0x10, (byte) 0xe0, (byte) 0xe7, (byte) 0x9f, (byte) 0x88, (byte) 0x4, (byte) 0x89, (byte) 0x33,
(byte) 0x9d, (byte) 0xb2, (byte) 0x14, (byte) 0xb9, (byte) 0x5e, (byte) 0x52, (byte) 0xf5, (byte) 0xc7,
(byte) 0x9a, (byte) 0xc2, (byte) 0xcf, (byte) 0xa, (byte) 0x38, (byte) 0xc5, (byte) 0xd0, (byte) 0xef,
(byte) 0xb2, (byte) 0x37, (byte) 0x1d, (byte) 0x55, (byte) 0xcb, (byte) 0xfd, (byte) 0xa4, (byte) 0x9a,
(byte) 0xb9, (byte) 0x7d, (byte) 0x9a, (byte) 0x10, (byte) 0x4e, (byte) 0x33, (byte) 0x64, (byte) 0x72,
(byte) 0x77, (byte) 0x6d, (byte) 0xc5, (byte) 0xb2, (byte) 0x73, (byte) 0xda, (byte) 0xba, (byte) 0xac,
(byte) 0xd7, (byte) 0xf9, (byte) 0xe4, (byte) 0xb6, (byte) 0xb1, (byte) 0xa1, (byte) 0x6b, (byte) 0x86,
(byte) 0x5c, (byte) 0xb7, (byte) 0x73, (byte) 0x1b, (byte) 0xb5, (byte) 0x29, (byte) 0xab, (byte) 0x6e,
(byte) 0xca, (byte) 0xe4, (byte) 0x48, (byte) 0x53, (byte) 0xab, (byte) 0xf6, (byte) 0xe4, (byte) 0xba,
(byte) 0x6c, (byte) 0x89, (byte) 0xe8, (byte) 0x45, (byte) 0x9c, (byte) 0x47, (byte) 0xdc, (byte) 0x47,
(byte) 0x9c, (byte) 0xa9, (byte) 0x42, (byte) 0x3b, (byte) 0xe6, (byte) 0x92, (byte) 0x63, (byte) 0x91,
(byte) 0xc7, (byte) 0xfc, (byte) 0xf8, (byte) 0x6a, (byte) 0x18, (byte) 0xfd, (byte) 0x38, (byte) 0x2b,
(byte) 0xe0, (byte) 0x9c, (byte) 0x84, (byte) 0x1, (byte) 0x24, (byte) 0x8e, (byte) 0xa4, (byte) 0x55,
(byte) 0x7a, (byte) 0x66, (byte) 0x3b, (byte) 0x4a, (byte) 0x83, (byte) 0xb2, (byte) 0x31, (byte) 0x9a,
(byte) 0x94, (byte) 0x5c, (byte) 0xbf, (byte) 0x7, (byte) 0x56, (byte) 0x8d, (byte) 0x5c, (byte) 0x91,
(byte) 0x90, (byte) 0xe, (byte) 0xe1, (byte) 0x15, (byte) 0xb9, (byte) 0x91, (byte) 0xf, (byte) 0xe1,
(byte) 0x3d, (byte) 0xc2, (byte) 0xf0, (byte) 0xd8, (byte) 0x46, (byte) 0x92, (byte) 0xf6, (byte) 0x8,
(byte) 0x25, (byte) 0xa9, (byte) 0x2b, (byte) 0xf5, (byte) 0xa4, (byte) 0x2e, (byte) 0x62, (byte) 0x8,
(byte) 0x17, (byte) 0x4, (byte) 0xc, (byte) 0x4b, (byte) 0x18, (byte) 0xc1, (byte) 0x45, (byte) 0x52,
(byte) 0xa1, (byte) 0xb, (byte) 0x92, (byte) 0xd4, (byte) 0x32, (byte) 0xf9, (byte) 0xea, (byte) 0x11,
(byte) 0xc3, (byte) 0x4c, (byte) 0x97, (byte) 0xb8, (byte) 0xee, (byte) 0x17, (byte) 0x8e, (byte) 0xcb,
(byte) 0x9b, (byte) 0x1f, (byte) 0xef, (byte) 0x1a, (byte) 0x0, (byte) 0x4f, (byte) 0x70, (byte) 0x54,
(byte) 0xc2, (byte) 0x18, (byte) 0x17, (byte) 0x32, (byte) 0x58, (byte) 0xd3, (byte) 0xc, (byte) 0x9b,
(byte) 0xf4, (byte) 0x7b, (byte) 0xff, (byte) 0xc0, (byte) 0xa5, (byte) 0xb, (byte) 0x2f, (byte) 0x3f,
(byte) 0xb1, (byte) 0x8c, (byte) 0xd, (byte) 0xb9, (byte) 0xaa, (byte) 0x29, (byte) 0x22, (byte) 0x92,
(byte) 0x48, (byte) 0x49, (byte) 0x18, (byte) 0xc7, (byte) 0x4, (byte) 0x43, (byte) 0xaf, (byte) 0x5c,
(byte) 0xaf, (byte) 0x97, (byte) 0x9a, (byte) 0xa6, (byte) 0x69, (byte) 0x29, (byte) 0xb6, (byte) 0xad,
(byte) 0xd4, (byte) 0x19, (byte) 0x6, (byte) 0x3a, (byte) 0x63, (byte) 0x38, (byte) 0x44, (byte) 0xe4,
(byte) 0x79, (byte) 0x19, (byte) 0x33, (byte) 0x1d, (byte) 0x9, (byte) 0xcc, (byte) 0x2f, (byte) 0xdf,
(byte) 0x79, (byte) 0x5a, (byte) 0x53, (byte) 0x4c, (byte) 0x47, (byte) 0x35, (byte) 0x74, (byte) 0x1,
(byte) 0x93, (byte) 0xc, (byte) 0x83, (byte) 0x6d, (byte) 0xd0, (byte) 0x4a, (byte) 0x53, (byte) 0x77,
(byte) 0xd4, (byte) 0x86, (byte) 0x72, (byte) 0xb8, (byte) 0x2f, (byte) 0x22, (byte) 0x8b, (byte) 0xf,
(byte) 0xb8, (byte) 0xf2, (byte) 0x13, (byte) 0x2, (byte) 0x3e, (byte) 0x3a, (byte) 0xaa, (byte) 0xab,
(byte) 0x9b, (byte) 0x62, (byte) 0x4, (byte) 0xd3, (byte) 0xf8, (byte) 0x58, (byte) 0xc0, (byte) 0x15,
(byte) 0x9, (byte) 0x9f, (byte) 0xe0, (byte) 0x53, (byte) 0x86, (byte) 0xb8, (byte) 0xbb, (byte) 0xaf,
(byte) 0x93, (byte) 0x8f, (byte) 0x47, (byte) 0x24, (byte) 0x66, (byte) 0xae, (byte) 0x28, (byte) 0x3b,
(byte) 0x4f, (byte) 0x18, (byte) 0x7c, (byte) 0x6, (byte) 0xc9, (byte) 0x93, (byte) 0xff, (byte) 0x1f,
(byte) 0x79, (byte) 0xe, (byte) 0x2a, (byte) 0x59, (byte) 0x78, (byte) 0x97, (byte) 0x21, (byte) 0x1f,
(byte) 0xc1, (byte) 0x55, (byte) 0xe4, (byte) 0x5, (byte) 0xcc, (byte) 0x70, (byte) 0x27, (byte) 0x9f,
(byte) 0x53, (byte) 0xd6, (byte) 0xed, (byte) 0xae, (byte) 0x5a, (byte) 0x94, (byte) 0x4d, (byte) 0x86,
(byte) 0x58, (byte) 0x6a, (byte) 0x1f, (byte) 0x75, (byte) 0x60, (byte) 0x22, (byte) 0x39, (byte) 0xaf,
(byte) 0xe3, (byte) 0x86, (byte) 0x80, (byte) 0x9b, (byte) 0x12, (byte) 0x75, (byte) 0xfc, (byte) 0x2c,
(byte) 0x43, (byte) 0xe2, (byte) 0x28, (byte) 0x27, (byte) 0xaf, (byte) 0xb3, (byte) 0xd7, (byte) 0x15,
(byte) 0x36, (byte) 0x91, (byte) 0xe9, (byte) 0xca, (byte) 0x46, (byte) 0xdb, (byte) 0xc0, (byte) 0x70,
(byte) 0x3d, (byte) 0xd5, (byte) 0x2d, (byte) 0x82, (byte) 0x63, (byte) 0xfc, (byte) 0xc7, (byte) 0xa3,
(byte) 0x6c, (byte) 0x13, (byte) 0x50, (byte) 0x2b, (byte) 0xdd, (byte) 0x61, (byte) 0x88, (byte) 0x74,
(byte) 0x74, (byte) 0x11, (byte) 0x57, (byte) 0xe8, (byte) 0xb, (byte) 0x9, (byte) 0x5f, (byte) 0x62,
(byte) 0x9e, (byte) 0xee, (byte) 0x88, (byte) 0x63, (byte) 0xdc, (byte) 0xac, (byte) 0xda, (byte) 0x86,
(byte) 0xd6, (byte) 0x74, (byte) 0x14, (byte) 0x4f, (byte) 0x9a, (byte) 0xb3, (byte) 0xa9, (byte) 0x13,
(byte) 0x12, (byte) 0x9e, (byte) 0xc6, (byte) 0x57, (byte) 0x12, (byte) 0xa, (byte) 0x58, (byte) 0x64,
(byte) 0x8, (byte) 0x39, (byte) 0x86, (byte) 0xa7, (byte) 0xd, (byte) 0xc3, (byte) 0x99, (byte) 0x83,
(byte) 0xd3, (byte) 0x9d, (byte) 0x8a, (byte) 0x89, (byte) 0x58, (byte) 0xc6, (byte) 0xa8, (byte) 0x80,
(byte) 0x22, (byte) 0x55, (byte) 0xff, (byte) 0xa4, (byte) 0xa0, (byte) 0x42, (byte) 0x58, (byte) 0xa1,
(byte) 0x22, (byte) 0x27, (byte) 0x75, (byte) 0xfe, (byte) 0x3f, (byte) 0x46, (byte) 0x7f, (byte) 0xbc,
(byte) 0xc9, (byte) 0x97, (byte) 0x96, (byte) 0xcb, (byte) 0x1d, (byte) 0x8d, (byte) 0x9e, (byte) 0x46,
(byte) 0x59, (byte) 0xc2, (byte) 0x5d, (byte) 0xde, (byte) 0x7d, (byte) 0x51, (byte) 0xd3, (byte) 0x6b,
(byte) 0x4b, (byte) 0xb9, (byte) 0xf6, (byte) 0x43, (byte) 0xd9, (byte) 0x92, (byte) 0x6b, (byte) 0xa,
(byte) 0x5, (byte) 0xb0, (byte) 0x36, (byte) 0x5f, (byte) 0x7c, (byte) 0x30, (byte) 0x37, (byte) 0x5f,
(byte) 0xa0, (byte) 0xbc, (byte) 0xe2, (byte) 0xef, (byte) 0xba, (byte) 0x27, (byte) 0x5, (byte) 0x67,
(byte) 0xd, (byte) 0xdd, (byte) 0x76, (byte) 0x64, (byte) 0xdd, (byte) 0x59, (byte) 0x95, (byte) 0xb5,
(byte) 0x26, (byte) 0x9d, (byte) 0xf, (byte) 0xcc, (byte) 0x1a, (byte) 0x75, (byte) 0x1a, (byte) 0xa2,
(byte) 0x5, (byte) 0x55, (byte) 0x57, (byte) 0x96, (byte) 0x9a, (byte) 0x8d, (byte) 0xaa, (byte) 0x62,
(byte) 0x95, (byte) 0x79, (byte) 0x17, (byte) 0xd2, (byte) 0x46, (byte) 0x43, (byte) 0x56, (byte) 0x75,
(byte) 0x9e, (byte) 0x73, (byte) 0xb7, (byte) 0xba, (byte) 0xaf, (byte) 0x12, (byte) 0x91, (byte) 0xeb,
(byte) 0x95, (byte) 0xf4, (byte) 0xdd, (byte) 0x3f, (byte) 0x2f, (byte) 0x96, (byte) 0x8c, (byte) 0xa6,
(byte) 0x55, (byte) 0x73, (byte) 0x5f, (byte) 0x13, (byte) 0x4a, (byte) 0xae, (byte) 0xcb, (byte) 0x1b,
(byte) 0x34, (byte) 0xc9, (byte) 0x69, (byte) 0x70, (byte) 0x91, (byte) 0x1e, (byte) 0x21, (byte) 0x1f,
(byte) 0x3d, (byte) 0x6e, (byte) 0x54, (byte) 0x67, (byte) 0xac, (byte) 0xe2, (byte) 0x1e, (byte) 0x8d,
(byte) 0xdf, (byte) 0xd0, (byte) 0xca, (byte) 0x87, (byte) 0x28, (byte) 0xfc, (byte) 0x34, (byte) 0xa7,
(byte) 0x57, (byte) 0x8e, (byte) 0xbe, (byte) 0x15, (byte) 0xb2, (byte) 0xc, (byte) 0xd1, (byte) 0xc8,
(byte) 0x68, (byte) 0xc, (byte) 0x4e, (byte) 0x6c, (byte) 0x83, (byte) 0x6d, (byte) 0xb9, (byte) 0x80,
(byte) 0x35, (byte) 0xfa, (byte) 0xf6, (byte) 0xb8, (byte) 0xc6, (byte) 0x10, (byte) 0xc2, (byte) 0xb8,
(byte) 0x8f, (byte) 0x6f, (byte) 0xf7, (byte) 0x8f, (byte) 0x5e, (byte) 0x26, (byte) 0x38, (byte) 0xb7,
(byte) 0x46, (byte) 0x76, (byte) 0x21, (byte) 0x54, (byte) 0xb6, (byte) 0x11, (byte) 0x6e, (byte) 0x41,
(byte) 0x6c, (byte) 0x3, (byte) 0x44, (byte) 0xd7, (byte) 0x9b, (byte) 0x4, (byte) 0x7e, (byte) 0x67,
(byte) 0x7d, (byte) 0xfc, (byte) 0xed, (byte) 0xf3, (byte) 0x40, (byte) 0xec, (byte) 0xf, (byte) 0x9a,
(byte) 0x5, (byte) 0x69, (byte) 0xe7, (byte) 0xc5, (byte) 0x2e, (byte) 0x7a, (byte) 0x2b, (byte) 0xf1,
(byte) 0xe8, (byte) 0x36, (byte) 0x62, (byte) 0x85, (byte) 0x17, (byte) 0x38, (byte) 0x13, (byte) 0x1f,
(byte) 0xc, (byte) 0xbc, (byte) 0x82, (byte) 0xaf, (byte) 0xe2, (byte) 0x8f, (byte) 0x47, (byte) 0x4b,
(byte) 0x2d, (byte) 0x9c, (byte) 0xbf, (byte) 0x97, (byte) 0x6e, (byte) 0xe1, (byte) 0xd2, (byte) 0x26,
(byte) 0xfa, (byte) 0x16, (byte) 0xbd, (byte) 0x31, (byte) 0xbc, (byte) 0x94, (byte) 0xc9, (byte) 0xb6,
(byte) 0x70, (byte) 0x39, (byte) 0xf3, (byte) 0x7a, (byte) 0x13, (byte) 0xbd, (byte) 0x85, (byte) 0x5d,
(byte) 0x64, (byte) 0x2b, (byte) 0xe9, (byte) 0x6d, (byte) 0xe4, (byte) 0x5e, (byte) 0xc7, (byte) 0xa3,
(byte) 0xfe, (byte) 0x57, (byte) 0xf8, (byte) 0x70, (byte) 0x7, (byte) 0x53, (byte) 0x85, (byte) 0xf4,
(byte) 0xe, (byte) 0x3e, (byte) 0xdb, (byte) 0xc1, (byte) 0xb5, (byte) 0x45, (byte) 0xce, (byte) 0x73,
(byte) 0xdb, (byte) 0xe3, (byte) 0x49, (byte) 0xbf, (byte) 0xc4, (byte) 0x1c, (byte) 0xc3, (byte) 0x4b,
(byte) 0x2c, (byte) 0x30, (byte) 0xb8, (byte) 0x84, (byte) 0x99, (byte) 0x37, (byte) 0x18, (byte) 0xc9,
(byte) 0xb4, (byte) 0xb0, (byte) 0xb4, (byte) 0x89, (byte) 0xc4, (byte) 0x12, (byte) 0xcd, (byte) 0xe3,
(byte) 0xde, (byte) 0x3c, (byte) 0x72, (byte) 0x35, (byte) 0x90, (byte) 0x4d, (byte) 0x4, (byte) 0x88,
(byte) 0x3a, (byte) 0x4b, (byte) 0xd4, (byte) 0xa3, (byte) 0x2e, (byte) 0xfe, (byte) 0xeb, (byte) 0x13,
(byte) 0xf0, (byte) 0x2d, (byte) 0x94, (byte) 0xb6, (byte) 0x28, (byte) 0x72, (byte) 0x11, (byte) 0xe7,
(byte) 0x31, (byte) 0x4a, (byte) 0x4f, (byte) 0xcb, (byte) 0x18, (byte) 0x52, (byte) 0xd4, (byte) 0xd,
(byte) 0x49, (byte) 0xca, (byte) 0x63, (byte) 0xa, (byte) 0x57, (byte) 0x68, (byte) 0xb6, (byte) 0x82,
(byte) 0x6, (byte) 0x9e, (byte) 0xd2, (byte) 0xfa, (byte) 0x67, (byte) 0xfc, (byte) 0x82, (byte) 0x5f,
(byte) 0x69, (byte) 0x5c, (byte) 0xc0, (byte) 0x6f, (byte) 0xf8, (byte) 0x9d, (byte) 0xec, (byte) 0x3e,
(byte) 0x57, (byte) 0x8b, (byte) 0x39, (byte) 0xc4, (byte) 0xe8, (byte) 0x1b, (byte) 0x23, (byte) 0xf4,
(byte) 0x39, (byte) 0xc2, (byte) 0xf, (byte) 0x10, (byte) 0x43, (byte) 0x8c, (byte) 0x70, (byte) 0x83,
(byte) 0x84, (byte) 0x1c, (byte) 0xa0, (byte) 0xbb, (byte) 0x9a, (byte) 0xc0, (byte) 0xd, (byte) 0xb2,
(byte) 0x2e, (byte) 0x90, (byte) 0xf2, (byte) 0x2b, (byte) 0xb8, (byte) 0x40, (byte) 0x3c, (byte) 0xc3,
(byte) 0xc4, (byte) 0x34, (byte) 0x44, (byte) 0xf8, (byte) 0x4b, (byte) 0xc4, (byte) 0x30, (byte) 0x8c,
(byte) 0xe7, (byte) 0xf4, (byte) 0xac, (byte) 0xfe, (byte) 0x49, (byte) 0xd5, (byte) 0xdb, (byte) 0x22,
(byte) 0xd4, (byte) 0x77, (byte) 0xc4, (byte) 0xf2, (byte) 0x10, (byte) 0xd2, (byte) 0x1e, (byte) 0x4d,
(byte) 0x7d, (byte) 0x2, (byte) 0x4, (byte) 0x81, (byte) 0xa4, (byte) 0x65, (byte) 0x2, (byte) 0x92,
(byte) 0x7b, (byte) 0xfc, (byte) 0x47, (byte) 0xc6, (byte) 0x5b, (byte) 0xd3, (byte) 0xca, (byte) 0x35,
(byte) 0x5, (byte) 0xff, (byte) 0x1, (byte) 0xbb, (byte) 0x25, (byte) 0x20, (byte) 0x1d, (byte) 0xde,
(byte) 0x23, (byte) 0x7, (byte) 0xde, (byte) 0xde, (byte) 0xb4, (byte) 0x40, (byte) 0x57, (byte) 0xc0,
(byte) 0x3b, (byte) 0x1e, (byte) 0x41, (byte) 0xe0, (byte) 0xd0, (byte) 0x74, (byte) 0x80, (byte) 0x10,
(byte) 0xf6, (byte) 0x11, (byte) 0xc3, (byte) 0x54, (byte) 0xa7, (byte) 0xef, (byte) 0xdd, (byte) 0xce,
(byte) 0x78, (byte) 0xf0, (byte) 0x1f, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d,
(byte) 0x3, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x0, (byte) 0x0, (byte) 0xc0,
(byte) 0x69, (byte) 0x1, (byte) 0x57, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x9,
(byte) 0x0, (byte) 0x8, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0xed, (byte) 0x41, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e,
(byte) 0x46, (byte) 0x2f, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xfe, (byte) 0xca,
(byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d, (byte) 0x3,
(byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0xbd, (byte) 0x69,
(byte) 0x1, (byte) 0x57, (byte) 0x71, (byte) 0xa7, (byte) 0x16, (byte) 0x53, (byte) 0x5b, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x68, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x14, (byte) 0x0,
(byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xa4, (byte) 0x81, (byte) 0x3f, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46,
(byte) 0x2f, (byte) 0x4d, (byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45, (byte) 0x53,
(byte) 0x54, (byte) 0x2e, (byte) 0x4d, (byte) 0x46, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d, (byte) 0x3, (byte) 0x2d, (byte) 0x0,
(byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x61, (byte) 0x69, (byte) 0x1, (byte) 0x57,
(byte) 0x16, (byte) 0x64, (byte) 0x9c, (byte) 0xc5, (byte) 0x0, (byte) 0x4, (byte) 0x0, (byte) 0x0,
(byte) 0xf, (byte) 0x7, (byte) 0x0, (byte) 0x0, (byte) 0x19, (byte) 0x0, (byte) 0x4, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0xa4, (byte) 0x81, (byte) 0xe0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5a, (byte) 0x69,
(byte) 0x70, (byte) 0x46, (byte) 0x69, (byte) 0x6c, (byte) 0x65, (byte) 0x50, (byte) 0x72, (byte) 0x6f,
(byte) 0x70, (byte) 0x65, (byte) 0x72, (byte) 0x74, (byte) 0x79, (byte) 0x54, (byte) 0x65, (byte) 0x73,
(byte) 0x74, (byte) 0x2e, (byte) 0x63, (byte) 0x6c, (byte) 0x61, (byte) 0x73, (byte) 0x73, (byte) 0x1,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x6, (byte) 0x6, (byte) 0x2c,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2d,
(byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xd0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2b, (byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x6, (byte) 0x7, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xfb, (byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50,
(byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3,
(byte) 0x0, (byte) 0x3, (byte) 0x0, (byte) 0xd0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2b,
(byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/*
* Jar file created by Ant specifying zip64mode="always" and createUnicodeExtraFields="always"
* ----------------#1--------------------
* [Central Directory Header]
* 0x51b: Signature : 0x02014b50
* 0x51f: Created Zip Spec : 0x2d [4.5]
* 0x520: Created OS : 0x3 [UNIX]
* 0x521: VerMadeby : 0x32d [3, 4.5]
* 0x522: VerExtract : 0x2d [4.5]
* 0x523: Flag : 0x800
* 0x525: Method : 0x8 [DEFLATED]
* 0x527: Last Mod Time : 0x570b3767 [Fri Aug 11 06:59:14 EDT 2023]
* 0x52b: CRC : 0x5e4fa53f
* 0x52f: Compressed Size : 0xffffffff
* 0x533: Uncompressed Size: 0xffffffff
* 0x537: Name Length : 0x10
* 0x539: Extra Length : 0x35
* Extra data:[01, 00, 18, 00, 87, 04, 00, 00, 00, 00, 00, 00, c7, 02, 00, 00, 00, 00, 00, 00, 15, 01, 00, 00, 00, 00, 00, 00, 75, 70, 15, 00, 01, 94, 82, 60, 61, 52, 65, 61, 64, 41, 6e, 74, 4a, 61, 72, 2e, 63, 6c, 61, 73, 73]
* [tag=0x0001, sz=24]
* ->ZIP64: size *0x487 csize *0x2c7 LOC Off *0x115
* [data= 87 04 00 00 00 00 00 00 c7 02 00 00 00 00 00 00 15 01 00 00 00 00 00 00 ]
* [tag=0x7075, sz=21]
* ->[Unknown tag]
* [data= 01 94 82 60 61 52 65 61 64 41 6e 74 4a 61 72 2e 63 6c 61 73 73 ]
* 0x53b: Comment Length : 0x0
* 0x53d: Disk Start : 0x0
* 0x53f: Attrs : 0x0
* 0x541: AttrsEx : 0x81a40000
* 0x545: Loc Header Offset: 0xffffffff
* 0x549: File Name : ReadAntJar.class
*/
public static byte[] ANT_ZIP64_UNICODE_EXTRA_JAR = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8,
(byte) 0x0, (byte) 0x0, (byte) 0x18, (byte) 0x7e, (byte) 0xe, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0x9, (byte) 0x0, (byte) 0x2a, (byte) 0x0, (byte) 0x4d, (byte) 0x45,
(byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0x1,
(byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xfe, (byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x75,
(byte) 0x70, (byte) 0xe, (byte) 0x0, (byte) 0x1, (byte) 0x8, (byte) 0xa1, (byte) 0x8c, (byte) 0x13,
(byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46,
(byte) 0x2f, (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0,
(byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x17, (byte) 0x7e, (byte) 0xe, (byte) 0x57, (byte) 0x31,
(byte) 0x59, (byte) 0x76, (byte) 0x4d, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x14, (byte) 0x0, (byte) 0x31, (byte) 0x0, (byte) 0x4d,
(byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f,
(byte) 0x4d, (byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45, (byte) 0x53, (byte) 0x54,
(byte) 0x2e, (byte) 0x4d, (byte) 0x46, (byte) 0x1, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x6e,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x61,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x75,
(byte) 0x70, (byte) 0x19, (byte) 0x0, (byte) 0x1, (byte) 0x85, (byte) 0x85, (byte) 0x84, (byte) 0x2,
(byte) 0x4d, (byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46,
(byte) 0x2f, (byte) 0x4d, (byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45, (byte) 0x53,
(byte) 0x54, (byte) 0x2e, (byte) 0x4d, (byte) 0x46, (byte) 0xf3, (byte) 0x4d, (byte) 0xcc, (byte) 0xcb,
(byte) 0x4c, (byte) 0x4b, (byte) 0x2d, (byte) 0x2e, (byte) 0xd1, (byte) 0xd, (byte) 0x4b, (byte) 0x2d,
(byte) 0x2a, (byte) 0xce, (byte) 0xcc, (byte) 0xcf, (byte) 0xb3, (byte) 0x52, (byte) 0x30, (byte) 0xd4,
(byte) 0x33, (byte) 0xe0, (byte) 0xe5, (byte) 0x72, (byte) 0xcc, (byte) 0x43, (byte) 0x12, (byte) 0x71,
(byte) 0x2c, (byte) 0x48, (byte) 0x4c, (byte) 0xce, (byte) 0x48, (byte) 0x55, (byte) 0x0, (byte) 0x8a,
(byte) 0x1, (byte) 0x25, (byte) 0xd, (byte) 0xd, (byte) 0xf4, (byte) 0xc, (byte) 0x4d, (byte) 0x12,
(byte) 0x73, (byte) 0xa, (byte) 0x32, (byte) 0x12, (byte) 0x79, (byte) 0xb9, (byte) 0x9c, (byte) 0x8b,
(byte) 0x52, (byte) 0x13, (byte) 0x4b, (byte) 0x52, (byte) 0x53, (byte) 0x74, (byte) 0x9d, (byte) 0x2a,
(byte) 0x81, (byte) 0xda, (byte) 0x2c, (byte) 0xf4, (byte) 0x80, (byte) 0x32, (byte) 0x7a, (byte) 0x86,
(byte) 0xda, (byte) 0x46, (byte) 0xba, (byte) 0x66, (byte) 0xa, (byte) 0x1a, (byte) 0xfe, (byte) 0x45,
(byte) 0x89, (byte) 0xc9, (byte) 0x39, (byte) 0xa9, (byte) 0xa, (byte) 0xce, (byte) 0xf9, (byte) 0x45,
(byte) 0x5, (byte) 0xf9, (byte) 0x45, (byte) 0x89, (byte) 0x25, (byte) 0x40, (byte) 0x63, (byte) 0x34,
(byte) 0x79, (byte) 0xb9, (byte) 0x78, (byte) 0xb9, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3,
(byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x67,
(byte) 0x37, (byte) 0xb, (byte) 0x57, (byte) 0x3f, (byte) 0xa5, (byte) 0x4f, (byte) 0x5e, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x10,
(byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x52, (byte) 0x65, (byte) 0x61, (byte) 0x64, (byte) 0x41,
(byte) 0x6e, (byte) 0x74, (byte) 0x4a, (byte) 0x61, (byte) 0x72, (byte) 0x2e, (byte) 0x63, (byte) 0x6c,
(byte) 0x61, (byte) 0x73, (byte) 0x73, (byte) 0x1, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x87,
(byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xc7,
(byte) 0x2, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x75,
(byte) 0x70, (byte) 0x15, (byte) 0x0, (byte) 0x1, (byte) 0x94, (byte) 0x82, (byte) 0x60, (byte) 0x61,
(byte) 0x52, (byte) 0x65, (byte) 0x61, (byte) 0x64, (byte) 0x41, (byte) 0x6e, (byte) 0x74, (byte) 0x4a,
(byte) 0x61, (byte) 0x72, (byte) 0x2e, (byte) 0x63, (byte) 0x6c, (byte) 0x61, (byte) 0x73, (byte) 0x73,
(byte) 0x75, (byte) 0x53, (byte) 0x5b, (byte) 0x57, (byte) 0x12, (byte) 0x51, (byte) 0x18, (byte) 0xdd,
(byte) 0x3, (byte) 0xc3, (byte) 0x1c, (byte) 0x18, (byte) 0x47, (byte) 0x41, (byte) 0xcc, (byte) 0xb,
(byte) 0x96, (byte) 0x89, (byte) 0x25, (byte) 0x89, (byte) 0x17, (byte) 0xa0, (byte) 0x8b, (byte) 0x76,
(byte) 0x41, (byte) 0x33, (byte) 0xab, (byte) 0x95, (byte) 0x99, (byte) 0x21, (byte) 0xb4, (byte) 0x84,
(byte) 0x65, (byte) 0x8b, (byte) 0x7a, (byte) 0x1a, (byte) 0x61, (byte) 0xb2, (byte) 0x51, (byte) 0x98,
(byte) 0x99, (byte) 0x35, (byte) 0xc, (byte) 0x76, (byte) 0x79, (byte) 0xea, (byte) 0xa1, (byte) 0x7e,
(byte) 0x8b, (byte) 0xcf, (byte) 0xf6, (byte) 0x80, (byte) 0x6b, (byte) 0xe5, (byte) 0x2a, (byte) 0xdf,
(byte) 0x7c, (byte) 0xe8, (byte) 0x47, (byte) 0x65, (byte) 0xdf, (byte) 0x19, (byte) 0x50, (byte) 0xb0,
(byte) 0x65, (byte) 0x33, (byte) 0x8b, (byte) 0xef, (byte) 0x70, (byte) 0xbe, (byte) 0xb3, (byte) 0xf7,
(byte) 0xfe, (byte) 0x6e, (byte) 0x73, (byte) 0x7e, (byte) 0xff, (byte) 0xf9, (byte) 0xf1, (byte) 0xb,
(byte) 0xc0, (byte) 0x2c, (byte) 0x1e, (byte) 0xc9, (byte) 0x90, (byte) 0x31, (byte) 0xc6, (byte) 0x70,
(byte) 0x4d, (byte) 0x86, (byte) 0x7, (byte) 0x63, (byte) 0xdc, (byte) 0x5c, (byte) 0xf7, (byte) 0x63,
(byte) 0x9c, (byte) 0x21, (byte) 0x26, (byte) 0x43, (byte) 0xc2, (byte) 0x8d, (byte) 0x0, (byte) 0x26,
(byte) 0x10, (byte) 0xf7, (byte) 0x63, (byte) 0x92, (byte) 0x61, (byte) 0x4a, (byte) 0xc6, (byte) 0x34,
(byte) 0x66, (byte) 0xb8, (byte) 0x33, (byte) 0xc1, (byte) 0x90, (byte) 0x94, (byte) 0xd1, (byte) 0x8d,
(byte) 0x14, (byte) 0xc3, (byte) 0x4d, (byte) 0x3f, (byte) 0x6e, (byte) 0xc9, (byte) 0x8, (byte) 0xe2,
(byte) 0xb6, (byte) 0x0, (byte) 0x69, (byte) 0x41, (byte) 0x37, (byte) 0x74, (byte) 0x67, (byte) 0x51,
(byte) 0x80, (byte) 0x37, (byte) 0x3e, (byte) 0xb9, (byte) 0x21, (byte) 0x40, (byte) 0x7c, (byte) 0x6a,
(byte) 0x96, (byte) 0x35, (byte) 0x1, (byte) 0xc1, (byte) 0x8c, (byte) 0x6e, (byte) 0x68, (byte) 0xd9,
(byte) 0x7a, (byte) 0x75, (byte) 0x53, (byte) 0xb3, (byte) 0xb, (byte) 0xea, (byte) 0x66, (byte) 0x85,
(byte) 0x3c, (byte) 0x62, (byte) 0x55, (byte) 0xd5, (byte) 0xd, (byte) 0x1, (byte) 0x3, (byte) 0xf1,
(byte) 0xb7, (byte) 0x99, (byte) 0x6d, (byte) 0x75, (byte) 0x57, (byte) 0x4d, (byte) 0x55, (byte) 0x54,
(byte) 0x63, (byte) 0x2b, (byte) 0x95, (byte) 0x77, (byte) 0x6c, (byte) 0xdd, (byte) 0xd8, (byte) 0x9a,
(byte) 0xe7, (byte) 0x44, (byte) 0xbf, (byte) 0xad, (byte) 0xa9, (byte) 0xe5, (byte) 0x55, (byte) 0xd5,
(byte) 0xae, (byte) 0x9, (byte) 0xe8, (byte) 0xce, (byte) 0x3b, (byte) 0x6a, (byte) 0x69, (byte) 0x67,
(byte) 0x4d, (byte) 0xb5, (byte) 0x5c, (byte) 0x2a, (byte) 0xa5, (byte) 0xc8, (byte) 0x70, (byte) 0x87,
(byte) 0x12, (byte) 0xa3, (byte) 0xf0, (byte) 0x14, (byte) 0x59, (byte) 0x80, (byte) 0x9c, (byte) 0x37,
(byte) 0xeb, (byte) 0x76, (byte) 0x49, (byte) 0x5b, (byte) 0xd6, (byte) 0xb9, (byte) 0x6c, (byte) 0x70,
(byte) 0x9d, (byte) 0x68, (byte) 0x8f, (byte) 0xd, (byte) 0x87, (byte) 0x98, (byte) 0x49, (byte) 0xae,
(byte) 0xab, (byte) 0x20, (byte) 0x8c, (byte) 0x3e, (byte) 0xc2, (byte) 0xb4, (byte) 0xdd, (byte) 0xa,
(byte) 0x86, (byte) 0xb8, (byte) 0xa7, (byte) 0x47, (byte) 0x35, (byte) 0x9c, (byte) 0x6d, (byte) 0xd5,
(byte) 0x76, (byte) 0xb4, (byte) 0x9a, (byte) 0x43, (byte) 0x40, (byte) 0x5b, (byte) 0x40, (byte) 0xbf,
(byte) 0x9b, (byte) 0x46, (byte) 0xdd, (byte) 0xd1, (byte) 0x2b, (byte) 0xa9, (byte) 0xcf, (byte) 0xba,
(byte) 0x95, (byte) 0x7a, (byte) 0xa3, (byte) 0x5b, (byte) 0x5c, (byte) 0x91, (byte) 0xf3, (byte) 0x67,
(byte) 0x19, (byte) 0xe6, (byte) 0x14, (byte) 0xdc, (byte) 0xc5, (byte) 0x3d, (byte) 0x1, (byte) 0x83,
(byte) 0xb1, (byte) 0x5a, (byte) 0xd4, (byte) 0xb4, (byte) 0x34, (byte) 0x43, (byte) 0x2b, (byte) 0x47,
(byte) 0x77, (byte) 0x75, (byte) 0x35, (byte) 0xda, (byte) 0x2, (byte) 0xc5, (byte) 0xa8, (byte) 0x90,
(byte) 0x50, (byte) 0xbb, (byte) 0x8a, (byte) 0xdc, (byte) 0xe6, (byte) 0xb6, (byte) 0x56, (byte) 0x72,
(byte) 0x18, (byte) 0xee, (byte) 0x2b, (byte) 0x78, (byte) 0x80, (byte) 0xb4, (byte) 0x82, (byte) 0x79,
(byte) 0x1e, (byte) 0xaf, (byte) 0xaf, (byte) 0x7d, (byte) 0x5e, (byte) 0x78, (byte) 0x6f, (byte) 0x9b,
(byte) 0x1f, (byte) 0x78, (byte) 0x29, (byte) 0xa, (byte) 0x16, (byte) 0xf0, (byte) 0xf0, (byte) 0xf4,
(byte) 0x48, (byte) 0x37, (byte) 0x53, (byte) 0x2f, (byte) 0x72, (byte) 0xcf, (byte) 0x3e, (byte) 0x96,
(byte) 0x34, (byte) 0xcb, (byte) 0xd1, (byte) 0x4d, (byte) 0x12, (byte) 0x1c, (byte) 0x8e, (byte) 0x19,
(byte) 0xfc, (byte) 0x1d, (byte) 0xa7, (byte) 0x27, (byte) 0x4a, (byte) 0x51, (byte) 0xb3, (byte) 0xb9,
(byte) 0x42, (byte) 0x2b, (byte) 0x72, (byte) 0xcc, (byte) 0x50, (byte) 0xb0, (byte) 0xc8, (byte) 0x25,
(byte) 0x43, (byte) 0xff, (byte) 0x36, (byte) 0x8e, (byte) 0x8a, (byte) 0x88, (byte) 0x5f, (byte) 0xdc,
(byte) 0xcd, (byte) 0x4e, (byte) 0xe8, (byte) 0xa7, (byte) 0x9a, (byte) 0xa3, (byte) 0x55, (byte) 0x69,
(byte) 0x3e, (byte) 0x66, (byte) 0xdd, (byte) 0x21, (byte) 0x42, (byte) 0xe6, (byte) 0x34, (byte) 0xf8,
(byte) 0x2b, (byte) 0x2, (byte) 0x3b, (byte) 0x44, (byte) 0xd1, (byte) 0xd4, (byte) 0xea, (byte) 0x7c,
(byte) 0x47, (byte) 0x4e, (byte) 0x1d, (byte) 0x6e, (byte) 0x1a, (byte) 0xae, (byte) 0xc5, (byte) 0x77,
(byte) 0xef, (byte) 0x4, (byte) 0x2c, (byte) 0x5c, (byte) 0x10, (byte) 0xa7, (byte) 0x73, (byte) 0x90,
(byte) 0xcd, (byte) 0x16, (byte) 0xcc, (byte) 0x4f, (byte) 0xfe, (byte) 0x47, (byte) 0xdd, (byte) 0x57,
(byte) 0xaa, (byte) 0x98, (byte) 0x35, (byte) 0x9a, (byte) 0x56, (byte) 0xb7, (byte) 0x5a, (byte) 0x2e,
(byte) 0xe7, (byte) 0xeb, (byte) 0x96, (byte) 0x65, (byte) 0x6b, (byte) 0xb5, (byte) 0x9a, (byte) 0x56,
(byte) 0x16, (byte) 0x30, (byte) 0xd4, (byte) 0x29, (byte) 0x7b, (byte) 0xd6, (byte) 0x26, (byte) 0xb7,
(byte) 0x82, (byte) 0xa0, (byte) 0xd5, (byte) 0x54, (byte) 0xa0, (byte) 0x2f, (byte) 0xa1, (byte) 0x60,
(byte) 0xab, (byte) 0x25, (byte) 0xd, (byte) 0x63, (byte) 0xf4, (byte) 0x89, (byte) 0xca, (byte) 0xe0,
(byte) 0x8f, (byte) 0x17, (byte) 0x2, (byte) 0x9f, (byte) 0x32, (byte) 0xd9, (byte) 0x4b, (byte) 0xb4,
(byte) 0x1b, (byte) 0xa1, (byte) 0x55, (byte) 0xa0, (byte) 0xd5, (byte) 0x37, (byte) 0x75, (byte) 0x0,
(byte) 0x61, (byte) 0x9f, (byte) 0xfe, (byte) 0x50, (byte) 0x91, (byte) 0x64, (byte) 0x25, (byte) 0xd7,
(byte) 0x29, (byte) 0x21, (byte) 0x80, (byte) 0x1, (byte) 0xc, (byte) 0xb6, (byte) 0xa0, (byte) 0x13,
(byte) 0x24, (byte) 0xc1, (byte) 0xbd, (byte) 0x5d, (byte) 0x87, (byte) 0xf0, (byte) 0x14, (byte) 0xf,
(byte) 0xe0, (byte) 0x6d, (byte) 0x40, (byte) 0x6c, (byte) 0x13, (byte) 0xf8, (byte) 0x15, (byte) 0x0,
(byte) 0xfc, (byte) 0xb4, (byte) 0x6, (byte) 0xc8, (byte) 0x33, (byte) 0x74, (byte) 0xaa, (byte) 0x2f,
(byte) 0xac, (byte) 0x92, (byte) 0x8, (byte) 0xa3, (byte) 0x93, (byte) 0x6f, (byte) 0x61, (byte) 0x5f,
(byte) 0xe6, (byte) 0x10, (byte) 0x52, (byte) 0x71, (byte) 0xfa, (byte) 0x0, (byte) 0x6c, (byte) 0x4d,
(byte) 0xc8, (byte) 0x7e, (byte) 0x87, (byte) 0x3f, (byte) 0x1c, (byte) 0x10, (byte) 0x7f, (byte) 0x42,
(byte) 0x2e, (byte) 0x7a, (byte) 0xa7, (byte) 0xf3, (byte) 0xd, (byte) 0x74, (byte) 0xbd, (byte) 0x9e,
(byte) 0x39, (byte) 0xc6, (byte) 0xcb, (byte) 0xc4, (byte) 0x31, (byte) 0xfa, (byte) 0x67, (byte) 0x1a,
(byte) 0x50, (byte) 0xf6, (byte) 0xb0, (byte) 0x94, (byte) 0x16, (byte) 0x13, (byte) 0x11, (byte) 0xb1,
(byte) 0x81, (byte) 0x9e, (byte) 0x3d, (byte) 0xcc, (byte) 0x35, (byte) 0x3d, (byte) 0xc9, (byte) 0xb4,
(byte) 0x18, (byte) 0x11, (byte) 0xb3, (byte) 0x11, (byte) 0xf1, (byte) 0x28, (byte) 0xed, (byte) 0x23,
(byte) 0xe8, (byte) 0x48, (byte) 0x1b, (byte) 0x1a, (byte) 0x4e, (byte) 0x4b, (byte) 0x89, (byte) 0x88,
(byte) 0xe4, (byte) 0x42, (byte) 0x19, (byte) 0xf7, (byte) 0x44, (byte) 0x7c, (byte) 0x47, (byte) 0x7b,
(byte) 0x88, (byte) 0xac, (byte) 0xf1, (byte) 0x0, (byte) 0xa1, (byte) 0x73, (byte) 0x1, (byte) 0x1a,
(byte) 0xe8, (byte) 0xdd, (byte) 0xa7, (byte) 0x64, (byte) 0x26, (byte) 0xe8, (byte) 0x42, (byte) 0x26,
(byte) 0xe9, (byte) 0x1e, (byte) 0xf6, (byte) 0x60, (byte) 0x14, (byte) 0x4b, (byte) 0xad, (byte) 0x75,
(byte) 0x85, (byte) 0x12, (byte) 0x5c, (byte) 0xc7, (byte) 0x6, (byte) 0x8a, (byte) 0xb4, (byte) 0x5f,
(byte) 0xc2, (byte) 0xaa, (byte) 0xbb, (byte) 0xf7, (byte) 0x62, (byte) 0x7, (byte) 0x6, (byte) 0x5d,
(byte) 0x52, (byte) 0x8f, (byte) 0x5b, (byte) 0x5e, (byte) 0x12, (byte) 0x5d, (byte) 0x64, (byte) 0x15,
(byte) 0xf2, (byte) 0x72, (byte) 0x6, (byte) 0xe7, (byte) 0x4, (byte) 0x5d, (byte) 0xf6, (byte) 0xa,
(byte) 0xad, (byte) 0x3b, (byte) 0xd4, (byte) 0x4e, (byte) 0x8e, (byte) 0x34, (byte) 0x11, (byte) 0xc2,
(byte) 0x17, (byte) 0xf4, (byte) 0xe2, (byte) 0x2b, (byte) 0x15, (byte) 0x1e, (byte) 0x21, (byte) 0x74,
(byte) 0xe, (byte) 0xf2, (byte) 0x9, (byte) 0x11, (byte) 0x45, (byte) 0x86, (byte) 0x61, (byte) 0x86,
(byte) 0xcb, (byte) 0xc, (byte) 0x57, (byte) 0x18, (byte) 0xef, (byte) 0x36, (byte) 0x19, (byte) 0x79,
(byte) 0x99, (byte) 0xcc, (byte) 0x73, (byte) 0xfa, (byte) 0x9d, (byte) 0x10, (byte) 0x43, (byte) 0x3a,
(byte) 0x77, (byte) 0x7c, (byte) 0x6, (byte) 0xf1, (byte) 0x9e, (byte) 0x50, (byte) 0x64, (byte) 0x4f,
(byte) 0xf3, (byte) 0xc, (byte) 0x78, (byte) 0xc2, (byte) 0x70, (byte) 0xb5, (byte) 0x9f, (byte) 0x3a,
(byte) 0x3a, (byte) 0x4a, (byte) 0xb2, (byte) 0x1e, (byte) 0x44, (byte) 0xff, (byte) 0x2, (byte) 0x50,
(byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d, (byte) 0x3, (byte) 0x2d, (byte) 0x0, (byte) 0x0,
(byte) 0x8, (byte) 0x0, (byte) 0x0, (byte) 0x18, (byte) 0x7e, (byte) 0xe, (byte) 0x57, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x9, (byte) 0x0, (byte) 0x32, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0xed,
(byte) 0x41, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x4d, (byte) 0x45, (byte) 0x54,
(byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0x1, (byte) 0x0,
(byte) 0x18, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xfe, (byte) 0xca, (byte) 0x0, (byte) 0x0, (byte) 0x75, (byte) 0x70,
(byte) 0xe, (byte) 0x0, (byte) 0x1, (byte) 0x8, (byte) 0xa1, (byte) 0x8c, (byte) 0x13, (byte) 0x4d,
(byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f,
(byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d, (byte) 0x3, (byte) 0x2d, (byte) 0x0,
(byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x17, (byte) 0x7e, (byte) 0xe, (byte) 0x57,
(byte) 0x31, (byte) 0x59, (byte) 0x76, (byte) 0x4d, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x14, (byte) 0x0, (byte) 0x39, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0xa4, (byte) 0x81, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x4d, (byte) 0x45,
(byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f, (byte) 0x4d,
(byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45, (byte) 0x53, (byte) 0x54, (byte) 0x2e,
(byte) 0x4d, (byte) 0x46, (byte) 0x1, (byte) 0x0, (byte) 0x18, (byte) 0x0, (byte) 0x6e, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x61, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x51, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x75, (byte) 0x70,
(byte) 0x19, (byte) 0x0, (byte) 0x1, (byte) 0x85, (byte) 0x85, (byte) 0x84, (byte) 0x2, (byte) 0x4d,
(byte) 0x45, (byte) 0x54, (byte) 0x41, (byte) 0x2d, (byte) 0x49, (byte) 0x4e, (byte) 0x46, (byte) 0x2f,
(byte) 0x4d, (byte) 0x41, (byte) 0x4e, (byte) 0x49, (byte) 0x46, (byte) 0x45, (byte) 0x53, (byte) 0x54,
(byte) 0x2e, (byte) 0x4d, (byte) 0x46, (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d,
(byte) 0x3, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x67,
(byte) 0x37, (byte) 0xb, (byte) 0x57, (byte) 0x3f, (byte) 0xa5, (byte) 0x4f, (byte) 0x5e, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x10,
(byte) 0x0, (byte) 0x35, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa4, (byte) 0x81, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0x52, (byte) 0x65, (byte) 0x61, (byte) 0x64, (byte) 0x41, (byte) 0x6e, (byte) 0x74,
(byte) 0x4a, (byte) 0x61, (byte) 0x72, (byte) 0x2e, (byte) 0x63, (byte) 0x6c, (byte) 0x61, (byte) 0x73,
(byte) 0x73, (byte) 0x1, (byte) 0x0, (byte) 0x18, (byte) 0x0, (byte) 0x87, (byte) 0x4, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xc7, (byte) 0x2, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x15, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x75, (byte) 0x70, (byte) 0x15,
(byte) 0x0, (byte) 0x1, (byte) 0x94, (byte) 0x82, (byte) 0x60, (byte) 0x61, (byte) 0x52, (byte) 0x65,
(byte) 0x61, (byte) 0x64, (byte) 0x41, (byte) 0x6e, (byte) 0x74, (byte) 0x4a, (byte) 0x61, (byte) 0x72,
(byte) 0x2e, (byte) 0x63, (byte) 0x6c, (byte) 0x61, (byte) 0x73, (byte) 0x73, (byte) 0x50, (byte) 0x4b,
(byte) 0x6, (byte) 0x6, (byte) 0x2c, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x57, (byte) 0x1,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x37, (byte) 0x4,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b,
(byte) 0x6, (byte) 0x7, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x8e, (byte) 0x5,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x3, (byte) 0x0, (byte) 0x3, (byte) 0x0, (byte) 0x57, (byte) 0x1,
(byte) 0x0, (byte) 0x0, (byte) 0x37, (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
/*
* ----------------#1--------------------
* [Central Directory Header]
* 0x47: Signature : 0x02014b50
* 0x4b: Created Zip Spec : 0x2d [4.5]
* 0x4c: Created OS : 0x3 [UNIX]
* 0x4d: VerMadeby : 0x32d [3, 4.5]
* 0x4e: VerExtract : 0x2d [4.5]
* 0x4f: Flag : 0x800
* 0x51: Method : 0x8 [DEFLATED]
* 0x53: Last Mod Time : 0x570375bc [Thu Aug 03 14:45:56 EDT 2023]
* 0x57: CRC : 0x0
* 0x5b: Compressed Size : 0x2
* 0x5f: Uncompressed Size: 0x0
* * 0x63: Name Length : 0x5
* 0x65: Extra Length : 0x12
* Extra data:[01, 00, 00, 00, 75, 70, 0a, 00, 01, ba, f7, eb, c1, 61, 2e, 74, 78, 74]
* [tag=0x0001, sz=0]
* ->ZIP64:
* [tag=0x7075, sz=10]
* ->[Unknown tag]
* [data= 01 ba f7 eb c1 61 2e 74 78 74 ]
* 0x67: Comment Length : 0x0
* 0x69: Disk Start : 0x0
* 0x6b: Attrs : 0x0
* 0x6d: AttrsEx : 0x81a40000
* 0x71: Loc Header Offset: 0x0
* 0x75: File Name : a.txt
*/
public static byte[] ANT_ZIP64_UNICODE_EXTRA_ZIP= {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0xbc, (byte) 0x75, (byte) 0x3, (byte) 0x57, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0x5, (byte) 0x0, (byte) 0x22, (byte) 0x0, (byte) 0x61, (byte) 0x2e,
(byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x75,
(byte) 0x70, (byte) 0xa, (byte) 0x0, (byte) 0x1, (byte) 0xba, (byte) 0xf7, (byte) 0xeb, (byte) 0xc1,
(byte) 0x61, (byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x3, (byte) 0x0, (byte) 0x50,
(byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x2d, (byte) 0x3, (byte) 0x2d, (byte) 0x0, (byte) 0x0,
(byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0xbc, (byte) 0x75, (byte) 0x3, (byte) 0x57, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x12, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xa4,
(byte) 0x81, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x61, (byte) 0x2e, (byte) 0x74,
(byte) 0x78, (byte) 0x74, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x75, (byte) 0x70,
(byte) 0xa, (byte) 0x0, (byte) 0x1, (byte) 0xba, (byte) 0xf7, (byte) 0xeb, (byte) 0xc1, (byte) 0x61,
(byte) 0x2e, (byte) 0x74, (byte) 0x78, (byte) 0x74, (byte) 0x50, (byte) 0x4b, (byte) 0x6, (byte) 0x6,
(byte) 0x2c, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x2d, (byte) 0x0, (byte) 0x2d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x45, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x47, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x6, (byte) 0x7,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x8c, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x1, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x45, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x47, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
};
// Name of Zip file and Jar File used by the test
public static final Path VALID_APK = Path.of("working-apk.zip");
public static final Path VALID_APACHE_COMPRESS_JAR =
Path.of("valid-apache-compress.jar");
public static final Path VALID_ANT_JAR =
Path.of("valid-ant-zip64-unicode-extrafields.jar");
public static final Path VALID_ANT_ZIP =
Path.of("valid-ant-zip64-unicode-extrafields.zip");
/**
* Setup method used to create the Zip and Jar files used by the test
* @throws IOException if an error occurs
*/
@BeforeAll
public static void setup() throws IOException {
Files.deleteIfExists(VALID_APK);
Files.deleteIfExists(VALID_APACHE_COMPRESS_JAR);
Files.deleteIfExists(VALID_ANT_JAR);
Files.deleteIfExists(VALID_ANT_ZIP);
// Create the Zip file to read
Files.write(VALID_APK, VALID_APK_FILE);
Files.write(VALID_APACHE_COMPRESS_JAR, COMMONS_COMPRESS_JAR);
Files.write(VALID_ANT_JAR, ANT_ZIP64_UNICODE_EXTRA_JAR);
Files.write(VALID_ANT_ZIP, ANT_ZIP64_UNICODE_EXTRA_ZIP);
}
/**
* Zip and Jars files to validate we can open
*/
private static Stream<Path> zipFilesToTest() {
return Stream.of(VALID_APK, VALID_APACHE_COMPRESS_JAR, VALID_ANT_JAR, VALID_ANT_ZIP);
}
/**
* Validate that a Zip file which contains an extra header with a data size
* 0f 0 can be opened using ZipFile
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("zipFilesToTest")
public void zipFilesToTest(Path jar) throws IOException {
try (ZipFile zf = new ZipFile(jar.toFile())) {
System.out.printf("%s opened%n", jar.toAbsolutePath());
} catch (IOException ie) {
System.out.printf("%n%n%n$$$$ %s NOT opened%n", jar.toAbsolutePath());
throw ie;
}
}
/**
* Validate that a Zip file which contains an extra header with a data size
* 0f 0 can be opened using ZipFS
* @throws IOException if an error occurs
*/
@ParameterizedTest
@MethodSource("zipFilesToTest")
public void readZipFSTest(Path jar) throws IOException {
try (FileSystem fs = FileSystems.newFileSystem(jar, Map.of())) {
System.out.printf("%s opened%n", jar.toAbsolutePath());
} catch (IOException ie) {
System.out.printf("%n%n%n$$$$ %s NOT opened%n", jar.toAbsolutePath());
throw ie;
}
}
/**
* Utility method which takes a byte array and converts to byte array
* declaration. For example:
* <pre>
* {@code
* var fooJar = Files.readAllBytes(Path.of("foo.jar"));
* var result = createByteArray(fooJar, "FOOBYTES");
* System.out.println(result);
* }
* </pre>
*
* @param bytes A byte array used to create a byte array declaration
* @param name Name to be used in the byte array declaration
* @return The formatted byte array declaration
*/
public static String createByteArray(byte[] bytes, String name) {
StringBuilder sb = new StringBuilder(bytes.length * 5);
Formatter fmt = new Formatter(sb);
fmt.format(" public static byte[] %s = {", name);
final int linelen = 8;
for (int i = 0; i < bytes.length; i++) {
if (i % linelen == 0) {
fmt.format("%n ");
}
fmt.format(" (byte) 0x%x,", bytes[i] & 0xff);
}
fmt.format("%n };%n");
return sb.toString();
}
}

View file

@ -0,0 +1,380 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4241361 4842702 4985614 6646605 5032358 6923692 6233323 8144977 8186464 4401122 8322830
@summary Make sure we can read a zip file.
@modules jdk.zipfs
@run junit ReadZip
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.Collections;
import java.util.HexFormat;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.junit.jupiter.api.Assertions.*;
public class ReadZip {
// ZIP file produced during tests
private Path zip = Path.of("read-zip.zip");
/**
* Create a sample ZIP file for use by tests
* @param name name of the ZIP file to create
* @return a sample ZIP file
* @throws IOException if an unexpected IOException occurs
*/
private Path createZip(String name) throws IOException {
Path zip = Path.of(name);
try (OutputStream out = Files.newOutputStream(zip);
ZipOutputStream zo = new ZipOutputStream(out)) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
}
return zip;
}
/**
* Delete the ZIP file produced after each test method
* @throws IOException if an unexpected IOException occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/**
* Make sure we throw NPE when calling getEntry or getInputStream with null params
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void nullPointerExceptionOnNullParams() throws IOException {
zip = createZip("null-params.zip");
try (ZipFile zf = new ZipFile(zip.toFile())) {
assertThrows(NullPointerException.class, () -> zf.getEntry(null));
assertThrows(NullPointerException.class, () -> zf.getInputStream(null));
// Sanity check that we can still read an entry
ZipEntry ze = zf.getEntry("file.txt");
assertNotNull(ze, "cannot read from zip file");
}
}
/**
* Read the zip file that has some garbage bytes padded at the end
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void bytesPaddedAtEnd() throws IOException {
zip = createZip("bytes-padded.zip");
// pad some bytes
try (OutputStream os = Files.newOutputStream(zip,
StandardOpenOption.APPEND)) {
os.write(1);
os.write(3);
os.write(5);
os.write(7);
}
try (ZipFile zf = new ZipFile(zip.toFile())) {
ZipEntry ze = zf.getEntry("file.txt");
assertNotNull(ze, "cannot read from zip file");
}
}
/**
* Verify that we can read a comment from the ZIP
* file's 'End of Central Directory' header
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void readZipFileComment() throws IOException {
// Create a zip file with a comment in the 'End of Central Directory' header
try (OutputStream out = Files.newOutputStream(zip);
ZipOutputStream zos = new ZipOutputStream(out)) {
ZipEntry ze = new ZipEntry("ZipEntry");
zos.putNextEntry(ze);
zos.write(1);
zos.write(2);
zos.write(3);
zos.write(4);
zos.closeEntry();
zos.setComment("This is the comment for testing");
}
// Read zip file comment
try (ZipFile zf = new ZipFile(zip.toFile())) {
ZipEntry ze = zf.getEntry("ZipEntry");
assertNotNull(ze, "cannot read entry from zip file");
assertEquals("This is the comment for testing", zf.getComment());
}
}
/**
* Verify that a directory entry can be found using the
* name 'directory/' as well as 'directory/'
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void readDirectoryEntries() throws IOException {
// Create a ZIP containing some directory entries
try (OutputStream fos = Files.newOutputStream(zip);
ZipOutputStream zos = new ZipOutputStream(fos)) {
// Add a META-INF directory with STORED compression type
ZipEntry metaInf = new ZipEntry("META-INF/");
metaInf.setMethod(ZipEntry.STORED);
metaInf.setSize(0);
metaInf.setCrc(0);
zos.putNextEntry(metaInf);
// Add a regular directory
ZipEntry dir = new ZipEntry("directory/");
zos.putNextEntry(dir);
zos.closeEntry();
}
// Verify directory lookups
try (ZipFile zf = new ZipFile(zip.toFile())) {
// Look up 'directory/' using the full name
ZipEntry ze = zf.getEntry("directory/");
assertNotNull(ze, "read entry \"directory/\" failed");
assertTrue(ze.isDirectory(), "read entry \"directory/\" failed");
assertEquals("directory/", ze.getName());
try (InputStream is = zf.getInputStream(ze)) {
is.available();
} catch (Exception x) {
x.printStackTrace();
}
// Look up 'directory/' without the trailing slash
ze = zf.getEntry("directory");
assertNotNull(ze, "read entry \"directory\" failed");
assertTrue(ze.isDirectory(), "read entry \"directory\" failed");
assertEquals("directory/", ze.getName());
try (InputStream is = zf.getInputStream(ze)) {
is.available();
} catch (Exception x) {
x.printStackTrace();
}
// Sanity check that also META-INF/ can be looked up with or without the trailing slash
assertNotNull(zf.getEntry("META-INF"));
assertNotNull(zf.getEntry("META-INF/"));
assertEquals(zf.getEntry("META-INF").getName(),
zf.getEntry("META-INF/").getName());
}
}
/**
* Throw a NoSuchFileException exception when reading a non-existing zip file
*/
@Test
public void nonExistingFile() {
File nonExistingFile = new File("non-existing-file-f6804460f.zip");
assertThrows(NoSuchFileException.class, () ->
new ZipFile(nonExistingFile));
}
/**
* Read a Zip file with a 'Zip64 End of Central Directory header' which was created
* using ZipFileSystem with the 'forceZIP64End' option.
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void readZip64EndZipFs() throws IOException {
// Create zip file with Zip64 end
Map<String, Object> env = Map.of("create", "true", "forceZIP64End", "true");
try (FileSystem fs = FileSystems.newFileSystem(zip, env)) {
Files.write(fs.getPath("hello"), "hello".getBytes());
}
// Read using ZipFile
try (ZipFile zf = new ZipFile(zip.toFile())) {
try (InputStream in = zf.getInputStream(zf.getEntry("hello"))) {
assertEquals("hello", new String(in.readAllBytes(), StandardCharsets.US_ASCII));
}
}
// Read using ZipFileSystem
try (FileSystem fs = FileSystems.newFileSystem(zip, Map.of())) {
assertEquals("hello", new String(Files.readAllBytes(fs.getPath("hello"))));
}
}
/**
* Read a zip file created via Info-ZIP in streaming mode,
* which includes a 'Zip64 End of Central Directory header'.
*
* @throws IOException if an unexpected IOException occurs
* @throws InterruptedException if an unexpected InterruptedException occurs
*/
@Test
public void readZip64EndInfoZIPStreaming() throws IOException, InterruptedException {
// ZIP created using: "echo -n hello | zip zip64.zip -"
// Hex encoded using: "cat zip64.zip | xxd -ps"
byte[] zipBytes = HexFormat.of().parseHex("""
504b03042d0000000000c441295886a61036ffffffffffffffff01001400
2d010010000500000000000000050000000000000068656c6c6f504b0102
1e032d0000000000c441295886a610360500000005000000010000000000
000001000000b011000000002d504b06062c000000000000001e032d0000
00000000000000010000000000000001000000000000002f000000000000
003800000000000000504b06070000000067000000000000000100000050
4b050600000000010001002f000000380000000000
""".replaceAll("\n","")
);
Files.write(zip, zipBytes);
try (ZipFile zf = new ZipFile(this.zip.toFile())) {
try (InputStream in = zf.getInputStream(zf.getEntry("-"))) {
String contents = new String(in.readAllBytes(), StandardCharsets.US_ASCII);
assertEquals("hello", contents);
}
}
}
/**
* Check that the available() method overriden by the input stream returned by
* ZipFile.getInputStream correctly returns the number of remaining uncompressed bytes
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void availableShouldReturnRemainingUncompressedBytes() throws IOException {
// The number of uncompressed bytes to write to the sample ZIP entry
final int expectedBytes = 512;
// Create a sample ZIP with deflated entry of a known uncompressed size
try (ZipOutputStream zo = new ZipOutputStream(Files.newOutputStream(zip))) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write(new byte[expectedBytes]);
}
// Verify the behavior of ZipFileInflaterInputStream.available()
try (ZipFile zf = new ZipFile(zip.toFile())) {
ZipEntry e = zf.getEntry("file.txt");
try (InputStream in = zf.getInputStream(e)) {
// Initially, available() should return the full uncompressed size of the entry
assertEquals(expectedBytes, in.available(),
"wrong initial return value of available");
// Reading a few bytes should reduce the number of available bytes accordingly
int bytesToRead = 10;
in.read(new byte[bytesToRead]);
assertEquals(expectedBytes - bytesToRead, in.available());
// Reading all remaining bytes should reduce the number of available bytes to zero
in.transferTo(OutputStream.nullOutputStream());
assertEquals(0, in.available());
// available on a closed input stream should return zero
in.close();
assertEquals(0, in.available());
}
}
}
/**
* Verify that reading an InputStream from a closed ZipFile
* throws IOException as expected and does not crash the VM.
* See bugs: 4528128 6846616
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void readAfterClose() throws IOException {
zip = createZip("read-after-close.zip");
InputStream in;
try (ZipFile zf = new ZipFile(zip.toFile())) {
ZipEntry zent = zf.getEntry("file.txt");
in = zf.getInputStream(zent);
}
// zf is closed at this point
assertThrows(IOException.class, () -> {
in.read();
});
assertThrows(IOException.class, () -> {
in.read(new byte[10]);
});
assertThrows(IOException.class, () -> {
byte[] buf = new byte[10];
in.read(buf, 0, buf.length);
});
assertThrows(IOException.class, () -> {
in.readAllBytes();
});
}
/**
* Verify that ZipFile can open a ZIP file with zero entries
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void noEntries() throws IOException {
// Create a ZIP file with no entries
try (ZipOutputStream zo = new ZipOutputStream(Files.newOutputStream(zip))) {
}
// Open the "empty" ZIP file
try (ZipFile zf = new ZipFile(zip.toFile())) {
// Verify size
assertEquals(0, zf.size());
// Verify entry lookup using ZipFile.getEntry()
assertNull(zf.getEntry("file.txt"));
// Verify iteration using ZipFile.entries()
assertEquals(Collections.emptyList(), Collections.list(zf.entries()));
// Verify iteration using ZipFile.stream()
assertEquals(Collections.emptyList(), zf.stream().toList());
}
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 4214795
* @summary Make sure the same inflater will only be recycled
* once.
* @run junit ReleaseInflater
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ReleaseInflater {
// ZIP file produced in this test
private Path zip = Path.of("release-inflater.zip");
/**
* Create a sample ZIP file for use by tests
* @param name name of the ZIP file to create
* @return a sample ZIP file
* @throws IOException if an unexpected IOException occurs
*/
@BeforeEach
public void setUp() throws IOException {
try (ZipOutputStream zo = new ZipOutputStream(Files.newOutputStream(zip))) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("helloworld".getBytes(StandardCharsets.UTF_8));
}
}
/**
* Delete the ZIP and JAR files produced after each test method
* @throws IOException if an unexpected IOException occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
}
/**
* Verify that the same Inflater is not recycled across input streams
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void recycleInflaterOnlyOnce() throws IOException {
try (ZipFile zf = new ZipFile(zip.toFile())) {
ZipEntry e = zf.getEntry("file.txt");
InputStream in1 = zf.getInputStream(e);
// close the stream, the inflater will be released
in1.close();
// close the stream again, should be no-op
in1.close();
// create two new streams, allocating inflaters
InputStream in2 = zf.getInputStream(e);
InputStream in3 = zf.getInputStream(e);
// check to see if they influence each other
assertEquals(in2.read(), in3.read(), "Stream is corrupted!");
}
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 6285124
* @summary Small compressed zip entries should be read in one read() operation
* @author Martin Buchholz
*/
import java.io.*;
import java.util.zip.*;
public class ShortRead {
public static void main(String[] args) throws Exception {
final File zFile = new File("abc.zip");
try {
final String entryName = "abc";
final String data = "Data disponible";
try (FileOutputStream fos = new FileOutputStream(zFile);
ZipOutputStream zos = new ZipOutputStream(fos))
{
zos.putNextEntry(new ZipEntry(entryName));
zos.write(data.getBytes("ASCII"));
zos.closeEntry();
}
try (ZipFile zipFile = new ZipFile(zFile)) {
final ZipEntry zentry = zipFile.getEntry(entryName);
final InputStream inputStream = zipFile.getInputStream(zentry);
System.out.printf("size=%d csize=%d available=%d%n",
zentry.getSize(),
zentry.getCompressedSize(),
inputStream.available());
byte[] buf = new byte[data.length()];
final int count = inputStream.read(buf);
if (! new String(buf, "ASCII").equals(data) ||
count != data.length())
throw new Exception("short read?");
}
} finally {
zFile.delete();
}
}
}

View file

@ -0,0 +1,191 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @run junit StreamZipEntriesTest
* @summary Make sure we can stream entries of a zip file.
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.*;
public class StreamZipEntriesTest {
// ZIP file produced in this test
private Path zip = Path.of("stream.zip");
// JAR file produced in this test
private Path jar = Path.of("stream.jar");
/**
* Create sample ZIP and JAR files used in in this test
* @throws IOException if an unexpected IOException occurs
*/
@BeforeEach
public void setUp() throws IOException {
try (OutputStream out = Files.newOutputStream(zip);
ZipOutputStream zo = new ZipOutputStream(out)) {
zo.putNextEntry(new ZipEntry("entry1.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
zo.putNextEntry(new ZipEntry("entry2.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
}
try (OutputStream out = Files.newOutputStream(jar);
ZipOutputStream zo = new ZipOutputStream(out)) {
// A JAR file may start with a META-INF/ directory before the manifest
zo.putNextEntry(new ZipEntry("META-INF/"));
// Write the manifest
zo.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
new Manifest().write(zo);
// Write two regular entries
zo.putNextEntry(new ZipEntry("entry1.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
zo.putNextEntry(new ZipEntry("entry2.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
}
}
/**
* Delete the ZIP file produced after each test method
* @throws IOException if an unexpected IOException occurs
*/
@AfterEach
public void cleanup() throws IOException {
Files.deleteIfExists(zip);
Files.deleteIfExists(jar);
}
/**
* Verify that ZipFile.stream() produces the expected entries
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void testStreamZip() throws IOException {
Set<String> names = new HashSet<>(Set.of("entry1.txt", "entry2.txt"));
try (ZipFile zf = new ZipFile(zip.toFile())) {
zf.stream().forEach(e -> {
assertTrue(e instanceof ZipEntry);
String name = e.getName();
assertNotNull(names.remove(name));
String toString = e.toString();
assertEquals(name, toString);
});
// Check that all expected names were processed
assertTrue(names.isEmpty());
// Check that Stream.toArray produces the expected result
Object elements[] = zf.stream().toArray();
assertEquals(2, elements.length);
assertEquals(elements[0].toString(), "entry1.txt");
assertEquals(elements[1].toString(), "entry2.txt");
}
}
/**
* Verify that JarFile.stream() produces the expected entries
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void testStreamJar() throws IOException {
try (JarFile jf = new JarFile(jar.toFile())) {
Set<String> names = new HashSet<>(Set.of(
"META-INF/",
"META-INF/MANIFEST.MF",
"entry1.txt",
"entry2.txt"
));
jf.stream().forEach(e -> {
assertTrue(e instanceof JarEntry);
String name = e.getName();
assertNotNull(names.remove(name));
String toString = e.toString();
assertEquals(name, toString);
}
);
// Check that all expected names were processed
assertTrue(names.isEmpty(), "Unprocessed entries: " + names);
// Check that Stream.toArray produces the expected result
Object elements[] = jf.stream().toArray();
assertEquals(4, elements.length);
assertEquals(elements[0].toString(), "META-INF/");
assertEquals(elements[1].toString(), "META-INF/MANIFEST.MF");
assertEquals(elements[2].toString(), "entry1.txt");
assertEquals(elements[3].toString(), "entry2.txt");
}
}
/**
* Calling ZipFile.stream() on a closed ZipFile should throw ISE
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void testClosedZipFile() throws IOException {
ZipFile zf = new ZipFile(zip.toFile());
zf.close();
assertThrows(IllegalStateException.class, () -> {
Stream s = zf.stream();
});
}
/**
* Calling JarFile.stream() on a closed JarFile should throw ISE
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void testClosedJarFile() throws IOException {
JarFile jf = new JarFile(jar.toFile());
jf.close();
assertThrows(IllegalStateException.class, () -> {
Stream s = jf.stream();
});
}
}

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8185582 8197989
* @summary Check the resources of Inflater, Deflater and ZipFile are always
* cleaned/released when the instance is not unreachable
* @modules java.base/java.util.zip:open java.base/jdk.internal.vm.annotation
* @library /test/lib
* @comment The test relies on the Cleaner to invoke the cleaning actions. So
* we use "othervm" to prevent any Cleaner delays that could be contributed by any
* other tests or code that might have executed on the agentvm prior to this test
* execution
* @run main/othervm TestCleaner
*/
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.*;
import jdk.internal.vm.annotation.DontInline;
import jdk.test.lib.util.ForceGC;
import static java.nio.charset.StandardCharsets.US_ASCII;
public class TestCleaner {
public static void main(String[] args) throws Throwable {
testDeInflater();
testZipFile();
}
private static long addrOf(Object obj) {
try {
Field addr = obj.getClass().getDeclaredField("address");
if (!addr.trySetAccessible()) {
return -1;
}
return addr.getLong(obj);
} catch (Exception x) {
return -1;
}
}
// verify the "native resource" of In/Deflater has been cleaned
private static void testDeInflater() throws Throwable {
Field zsRefDef = Deflater.class.getDeclaredField("zsRef");
Field zsRefInf = Inflater.class.getDeclaredField("zsRef");
if (!zsRefDef.trySetAccessible() || !zsRefInf.trySetAccessible()) {
throw new RuntimeException("'zsRef' is not accessible");
}
if (addrOf(zsRefDef.get(new Deflater())) == -1 ||
addrOf(zsRefInf.get(new Inflater())) == -1) {
throw new RuntimeException("'addr' is not accessible");
}
List<Object> list = new ArrayList<>();
byte[] buf1 = new byte[1024];
byte[] buf2 = new byte[1024];
for (int i = 0; i < 10; i++) {
var def = new Deflater();
list.add(zsRefDef.get(def));
def.setInput("hello".getBytes());
def.finish();
int n = def.deflate(buf1);
var inf = new Inflater();
list.add(zsRefInf.get(inf));
inf.setInput(buf1, 0, n);
n = inf.inflate(buf2);
if (!"hello".equals(new String(buf2, 0, n))) {
throw new RuntimeException("compression/decompression failed");
}
}
final AtomicLong numNotYetCleaned = new AtomicLong();
// trigger GC
final boolean resourcesCleaned = ForceGC.wait(() -> {
final long remaining = list.stream().filter(o -> addrOf(o) != 0).count();
numNotYetCleaned.set(remaining);
return remaining == 0;
});
if (!resourcesCleaned) {
throw new RuntimeException(numNotYetCleaned.get()
+ " resources haven't yet been cleaned");
}
}
@DontInline
private static Object openAndCloseZipFile(File zip) throws Throwable {
try {
try (var fos = new FileOutputStream(zip);
var zos = new ZipOutputStream(fos)) {
zos.putNextEntry(new ZipEntry("hello"));
zos.write("hello".getBytes(US_ASCII));
zos.closeEntry();
}
var zf = new ZipFile(zip);
var es = zf.entries();
while (es.hasMoreElements()) {
zf.getInputStream(es.nextElement()).read();
}
Field fieldRes = ZipFile.class.getDeclaredField("res");
if (!fieldRes.trySetAccessible()) {
throw new RuntimeException("'ZipFile.res' is not accesible");
}
Object zfRes = fieldRes.get(zf);
if (zfRes == null) {
throw new RuntimeException("'ZipFile.res' is null");
}
Field fieldZsrc = zfRes.getClass().getDeclaredField("zsrc");
if (!fieldZsrc.trySetAccessible()) {
throw new RuntimeException("'ZipFile.zsrc' is not accesible");
}
return fieldZsrc.get(zfRes);
} finally {
zip.delete();
}
}
private static void testZipFile() throws Throwable {
File dir = new File(System.getProperty("test.dir", "."));
File zip = File.createTempFile("testzf", "zip", dir);
Object zsrc = openAndCloseZipFile(zip);
if (zsrc != null) {
Field zfileField = zsrc.getClass().getDeclaredField("zfile");
if (!zfileField.trySetAccessible()) {
throw new RuntimeException("'ZipFile.Source.zfile' is not accessible");
}
final boolean resourceCleaned = ForceGC.wait(() -> {
try {
return zfileField.get(zsrc) == null;
} catch (IllegalAccessException e) {
// shouldn't happen
throw new RuntimeException(e);
}
});
if (!resourceCleaned) {
throw new RuntimeException("cleaner failed to clean zipfile " + zip);
}
}
}
}

View file

@ -0,0 +1,358 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8142508 8146431
* @modules java.base/java.util.zip:open
* @summary Tests various ZipFile apis
* @run main/manual TestZipFile
*/
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class TestZipFile {
private static final Random r = new Random();
private static final int N = 50;
private static final int NN = 10;
private static final int ENUM = 10000;
private static final int ESZ = 10000;
private static ExecutorService executor = Executors.newFixedThreadPool(20);
private static final Set<Path> paths = new HashSet<>();
private static final boolean isWindows = System.getProperty("os.name")
.startsWith("Windows");
static void realMain (String[] args) throws Throwable {
try {
for (int i = 0; i < N; i++) {
test(r.nextInt(ENUM), r.nextInt(ESZ), false, true);
test(r.nextInt(ENUM), r.nextInt(ESZ), true, true);
}
executor.shutdown();
executor.awaitTermination(10, TimeUnit.MINUTES);
executor = Executors.newFixedThreadPool(20);
for (int i = 0; i < NN; i++) {
test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true);
test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true);
if(!isWindows) {
testCachedDelete();
testCachedOverwrite();
}
}
test(70000, 1000, false, true); // > 65536 entry number;
testDelete(); // OPEN_DELETE
executor.shutdown();
executor.awaitTermination(10, TimeUnit.MINUTES);
} finally {
for (Path path : paths) {
Files.deleteIfExists(path);
}
}
}
static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) {
String name = "test-" + r.nextInt() + ".zip";
Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld);
for (int i = 0; i < NN; i++) {
executor.submit(() -> doTest(zip));
}
}
// test scenario:
// (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE
// (2) test the ZipFile works correctly
// (3) check the zip is deleted after ZipFile gets closed
static void testDelete() throws Throwable {
String name = "testDelete-" + r.nextInt() + ".zip";
Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
try (ZipFile zf = new ZipFile(new File(zip.name),
ZipFile.OPEN_READ | ZipFile.OPEN_DELETE ))
{
doTest0(zip, zf);
}
Path p = Paths.get(name);
if (Files.exists(p)) {
fail("Failed to delete " + name + " with OPEN_DELETE");
}
}
// test scenario:
// (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it
// (2) delete zip1 and create zip2 with the same name the zip1 with zip2
// (3) zip1 tests should fail, but no crash
// (4) zip2 tasks should all get zip2, then pass normal testing.
static void testCachedDelete() throws Throwable {
String name = "testCachedDelete-" + r.nextInt() + ".zip";
Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
try (ZipFile zf = new ZipFile(zip1.name)) {
for (int i = 0; i < NN; i++) {
executor.submit(() -> verifyNoCrash(zip1));
}
// delete the "zip1" and create a new one to test
Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
/*
System.out.println("========================================");
System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n",
zip1.name, zip1.lastModified, zip1.entries.size(),
zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis());
System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n",
zip2.name, zip2.lastModified, zip2.entries.size(),
zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis());
*/
for (int i = 0; i < NN; i++) {
executor.submit(() -> doTest(zip2));
}
}
}
// overwrite the "zip1" and create a new one to test. So the two zip files
// have the same fileKey, but probably different lastModified()
static void testCachedOverwrite() throws Throwable {
String name = "testCachedOverWrite-" + r.nextInt() + ".zip";
Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
try (ZipFile zf = new ZipFile(zip1.name)) {
for (int i = 0; i < NN; i++) {
executor.submit(() -> verifyNoCrash(zip1));
}
// overwrite the "zip1" with new contents
Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false);
for (int i = 0; i < NN; i++) {
executor.submit(() -> doTest(zip2));
}
}
}
// just check the entries and contents. since the file has been either overwritten
// or deleted/rewritten, we only care if it crahes or not.
static void verifyNoCrash(Zip zip) throws RuntimeException {
try (ZipFile zf = new ZipFile(zip.name)) {
List<ZipEntry> zlist = new ArrayList<>(zip.entries.keySet());
String[] elist = zf.stream().map(e -> e.getName()).toArray(String[]::new);
if (!Arrays.equals(elist,
zlist.stream().map( e -> e.getName()).toArray(String[]::new)))
{
//System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n",
// zf.getName(), elist.length, zlist.size());
return;
}
for (ZipEntry ze : zlist) {
byte[] zdata = zip.entries.get(ze);
ZipEntry e = zf.getEntry(ze.getName());
if (e != null) {
checkEqual(e, ze);
if (!e.isDirectory()) {
// check with readAllBytes
try (InputStream is = zf.getInputStream(e)) {
if (!Arrays.equals(zdata, is.readAllBytes())) {
//System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n",
// zf.getName(), ze.getName());
}
}
}
}
}
} catch (Throwable t) {
// t.printStackTrace();
// fail(t.toString());
}
}
static void checkEqual(ZipEntry x, ZipEntry y) {
if (x.getName().equals(y.getName()) &&
x.isDirectory() == y.isDirectory() &&
x.getMethod() == y.getMethod() &&
(x.getTime() / 2000) == y.getTime() / 2000 &&
x.getSize() == y.getSize() &&
x.getCompressedSize() == y.getCompressedSize() &&
x.getCrc() == y.getCrc() &&
x.getComment().equals(y.getComment())
) {
pass();
} else {
fail(x + " not equal to " + y);
System.out.printf(" %s %s%n", x.getName(), y.getName());
System.out.printf(" %d %d%n", x.getMethod(), y.getMethod());
System.out.printf(" %d %d%n", x.getTime(), y.getTime());
System.out.printf(" %d %d%n", x.getSize(), y.getSize());
System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize());
System.out.printf(" %d %d%n", x.getCrc(), y.getCrc());
System.out.println("-----------------");
}
}
static void doTest(Zip zip) throws RuntimeException {
//Thread me = Thread.currentThread();
try (ZipFile zf = new ZipFile(zip.name)) {
doTest0(zip, zf);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
static void doTest0(Zip zip, ZipFile zf) throws Throwable {
// (0) check zero-length entry name, no AIOOBE
try {
check(zf.getEntry("") == null);
} catch (Throwable t) {
unexpected(t);
}
List<ZipEntry> list = new ArrayList<>(zip.entries.keySet());
// (1) check entry list, in expected order
if (!check(Arrays.equals(
list.stream().map( e -> e.getName()).toArray(String[]::new),
zf.stream().map( e -> e.getName()).toArray(String[]::new)))) {
return;
}
// (2) shuffle, and check each entry and its bytes
Collections.shuffle(list);
for (ZipEntry ze : list) {
byte[] data = zip.entries.get(ze);
ZipEntry e = zf.getEntry(ze.getName());
checkEqual(e, ze);
if (!e.isDirectory()) {
// check with readAllBytes
try (InputStream is = zf.getInputStream(e)) {
check(Arrays.equals(data, is.readAllBytes()));
}
// check with smaller sized buf
try (InputStream is = zf.getInputStream(e)) {
byte[] buf = new byte[(int)e.getSize()];
int sz = r.nextInt((int)e.getSize()/4 + 1) + 1;
int off = 0;
int n;
while ((n = is.read(buf, off, buf.length - off)) > 0) {
off += n;
}
check(is.read() == -1);
check(Arrays.equals(data, buf));
}
}
}
}
private static class Zip {
final String name;
final Map<ZipEntry, byte[]> entries;
BasicFileAttributes attrs;
long lastModified;
Zip(String name, int num, int szMax, boolean prefix, boolean clean) {
this.name = name;
entries = new LinkedHashMap<>(num);
try {
Path p = Paths.get(name);
if (clean) {
Files.deleteIfExists(p);
}
paths.add(p);
} catch (Exception x) {
throw new RuntimeException(x);
}
try (FileOutputStream fos = new FileOutputStream(name);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos))
{
if (prefix) {
byte[] bytes = new byte[r.nextInt(1000)];
r.nextBytes(bytes);
bos.write(bytes);
}
CRC32 crc = new CRC32();
for (int i = 0; i < num; i++) {
String ename = "entry-" + i + "-name-" + r.nextLong();
ZipEntry ze = new ZipEntry(ename);
int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED;
writeEntry(zos, crc, ze, method, szMax);
}
// add some manifest entries
for (int i = 0; i < r.nextInt(20); i++) {
String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong();
ZipEntry ze = new ZipEntry(meta);
writeEntry(zos, crc, ze, ZipEntry.STORED, szMax);
}
} catch (Exception x) {
throw new RuntimeException(x);
}
try {
this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class);
this.lastModified = new File(name).lastModified();
} catch (Exception x) {
throw new RuntimeException(x);
}
}
private void writeEntry(ZipOutputStream zos, CRC32 crc,
ZipEntry ze, int method, int szMax)
throws IOException
{
ze.setMethod(method);
byte[] data = new byte[r.nextInt(szMax + 1)];
r.nextBytes(data);
if (method == ZipEntry.STORED) { // must set size/csize/crc
ze.setSize(data.length);
ze.setCompressedSize(data.length);
crc.reset();
crc.update(data);
ze.setCrc(crc.getValue());
}
ze.setTime(System.currentTimeMillis());
ze.setComment(ze.getName());
zos.putNextEntry(ze);
zos.write(data);
zos.closeEntry();
entries.put(ze, data);
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void pass(String msg) {System.out.println(msg); passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
public static void main(String[] args) {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,339 @@
/*
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8243254
* @summary Tests a simple set of operations on Zip files in various encodings
* focusing on ensuring metadata is properly encoded and read.
* @run junit/timeout=480 TestZipFileEncodings
*/
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestZipFileEncodings {
private static int NUM_ENTRIES = 100;
private static int METAINF_ENTRIES = 5;
private static int ENTRY_SIZE = 100;
private static final AtomicInteger SEQUENCE = new AtomicInteger(0);
private static Set<Path> paths = new HashSet<>();
private static Random random() {
return ThreadLocalRandom.current();
}
public static Stream<String> nonUnicodeCharsets() {
return Stream.of(
"ISO-8859-1",
"IBM01149",
"IBM037",
"IBM-Thai"
);
}
public static Stream<String> unicodeCharsets() {
return Stream.of(
"UTF-8",
"UTF-16",
"UTF-16LE",
"UTF-16BE",
"UTF-32"
);
}
public static Stream<String> allCharsets() {
return Stream.concat(nonUnicodeCharsets(), unicodeCharsets());
}
@ParameterizedTest
@MethodSource("nonUnicodeCharsets")
public void testNonUnicode(String charsetName) throws Throwable {
test(NUM_ENTRIES, 100 + random().nextInt(ENTRY_SIZE), false, Charset.forName(charsetName));
}
@ParameterizedTest
@MethodSource("unicodeCharsets")
public void testUnicode(String charsetName) throws Throwable {
test(NUM_ENTRIES, 100 + random().nextInt(ENTRY_SIZE), true, Charset.forName(charsetName));
}
@ParameterizedTest
@MethodSource("nonUnicodeCharsets")
public void testNonUnicodeManyEntries(String charsetName) throws Throwable {
test(70000, 10, false, Charset.forName(charsetName));
}
@ParameterizedTest
@MethodSource("unicodeCharsets")
public void testUnicodeManyEntries(String charsetName) throws Throwable {
test(70000, 10, true, Charset.forName(charsetName));
}
/**
* This test was added to catch a regression where UTFZipCoder incorrectly
* treated latin1-encoded Strings as UTF8-compatible, while this actually only
* holds for ASCII strings.
*
* The implementation of UTFZipCoder.compare was later changed to not depend on
* the String's coder. Let's keep this test around anyway, since it provokes
* a corner case which could be easily missed.
*/
@Test
public void latin1NotAscii() throws IOException {
Path zip = Path.of("latin1-not-ascii.zip");
// latin1, but not ASCII
String entryName = "smörgåsbord";
try (ZipOutputStream z = new ZipOutputStream(Files.newOutputStream(zip))) {
z.putNextEntry(new ZipEntry(entryName));
}
try (ZipFile z = new ZipFile(zip.toFile())) {
assertNotNull(z.getEntry(entryName));
}
}
/**
* This test was added to catch a regression where ZipCoder.compare did not
* properly verify that the lookup name is a prefix of the entry name. Because of
* this regression, any candidate name with identical lengths and a trailing
* '/' would be incorrectly considered a "directory match".
*
* Since this regression depends on both a hash collision and that the length of names
* are equal, it is rarely found in the wild. Let's keep this test around
* since it explicity provokes this rare condition.
*
*/
@ParameterizedTest
@MethodSource("allCharsets")
public void sameHashAndLengthDirLookup(String charsetName) throws IOException {
// Two directory names with colliding hash codes and same length
// (found in a brute force search)
String one = "_____1637461950/";
String two = "_____-408231241/";
// Create a ZIP containing the two directories
Charset charset = Charset.forName(charsetName);
Path zip = Path.of("hash-collision-slashmatch-utf16.zip");
try (ZipOutputStream z = new ZipOutputStream(Files.newOutputStream(zip), charset)) {
// Give the names different comments so they we can distinguish them
ZipEntry first = new ZipEntry(one);
first.setComment("Entry one");
z.putNextEntry(first);
ZipEntry second = new ZipEntry(two);
second.setComment("Entry two");
z.putNextEntry(second);
}
// Assert that "slashless" lookups returns the correct entry even
// when the directory names have colliding hash codes and equal lengths
try (ZipFile z = new ZipFile(zip.toFile(), charset)) {
ZipEntry second = z.getEntry("_____-408231241");
assertEquals("Entry two", second.getComment());
ZipEntry first = z.getEntry("_____1637461950");
assertEquals("Entry one", first.getComment());
}
}
@AfterAll
public static void tearDown() {
for (Path path : paths) {
path.toFile().deleteOnExit();
}
}
static void test(int numEntry, int szMax, boolean unicode, Charset cs) throws Throwable {
String name = "zfenc-" + SEQUENCE.incrementAndGet() + ".zip";
Zip zip = new Zip(name, numEntry, szMax, unicode, cs);
doTest(zip);
}
static void checkEqual(ZipEntry x, ZipEntry y) {
assertEquals(y.getName(), x.getName());
assertEquals(y.isDirectory(), x.isDirectory());
assertEquals(y.getMethod(), x.getMethod());
assertEquals(y.getTime() / 2000, (x.getTime() / 2000));
assertEquals(y.getSize(), x.getSize());
assertEquals(y.getCompressedSize(), x.getCompressedSize());
assertEquals(y.getCrc(), x.getCrc());
assertEquals(y.getComment(), x.getComment());
}
static void doTest(Zip zip) throws Throwable {
try (ZipFile zf = new ZipFile(zip.name, zip.cs)) {
doTest0(zip, zf);
}
}
static void doTest0(Zip zip, ZipFile zf) throws Throwable {
// (0) check zero-length entry name, no AIOOBE
assertEquals(null, zf.getEntry(""));
List<ZipEntry> list = new ArrayList(zip.entries.keySet());
// check each entry and its bytes
for (ZipEntry ze : list) {
byte[] data = zip.entries.get(ze);
String name = ze.getName();
ZipEntry e = zf.getEntry(name);
checkEqual(e, ze);
if (!e.isDirectory()) {
// check with readAllBytes
try (InputStream is = zf.getInputStream(e)) {
assertArrayEquals(is.readAllBytes(), data);
}
int slash = name.indexOf('/');
if (slash > 0) {
ZipEntry dir1 = zf.getEntry(name.substring(0, slash));
ZipEntry dir2 = zf.getEntry(name.substring(0, slash + 1));
assertNotNull(dir1);
assertNotNull(dir2);
assertTrue(dir1.isDirectory());
assertTrue(dir2.isDirectory());
checkEqual(dir1, dir2);
}
} else {
ZipEntry unslashLookup = zf.getEntry(name.substring(0, name.length() - 1));
checkEqual(e, unslashLookup);
}
}
}
private static class Zip {
String name;
Charset cs;
Map<ZipEntry, byte[]> entries;
BasicFileAttributes attrs;
long lastModified;
Zip(String name, int num, int szMax, boolean unicode, Charset cs) {
this.cs = cs;
this.name = name;
entries = new LinkedHashMap<>(num);
try {
Path p = Paths.get(name);
Files.deleteIfExists(p);
paths.add(p);
} catch (Exception x) {
throw (RuntimeException)x;
}
try (FileOutputStream fos = new FileOutputStream(name);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos, cs))
{
CRC32 crc = new CRC32();
for (int i = 0; i < num; i++) {
String ename = "entry-" + i + "-name-" + random().nextLong();
if (unicode) {
// Provokes compatibility issue with slash handling for
// non-ASCII compatible Unicode encodings
ename = ename + '\u2F2F';
zos.putNextEntry(new ZipEntry(ename + '/'));
ename = ename + '/' + ename;
}
ZipEntry ze = new ZipEntry(ename);
assertTrue(!ze.isDirectory());
writeEntry(zos, crc, ze, ZipEntry.STORED, szMax);
}
// add some manifest entries
zos.putNextEntry(new ZipEntry("META-INF/"));
for (int i = 0; i < METAINF_ENTRIES; i++) {
String meta = "META-INF/" + "entry-" + i + "-metainf-" + random().nextLong();
ZipEntry ze = new ZipEntry(meta);
writeEntry(zos, crc, ze, ZipEntry.STORED, szMax);
}
} catch (Exception x) {
throw (RuntimeException)x;
}
try {
this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class);
this.lastModified = new File(name).lastModified();
} catch (Exception x) {
throw (RuntimeException)x;
}
}
private void writeEntry(ZipOutputStream zos, CRC32 crc,
ZipEntry ze, int method, int szMax)
throws IOException
{
ze.setMethod(method);
byte[] data = new byte[random().nextInt(szMax + 1)];
random().nextBytes(data);
if (method == ZipEntry.STORED) { // must set size/csize/crc
ze.setSize(data.length);
ze.setCompressedSize(data.length);
crc.reset();
crc.update(data);
ze.setCrc(crc.getValue());
}
ze.setTime(System.currentTimeMillis());
ze.setComment(ze.getName());
zos.putNextEntry(ze);
zos.write(data);
zos.closeEntry();
entries.put(ze, data);
}
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import static java.util.zip.ZipFile.CENOFF;
import static java.util.zip.ZipFile.CENTIM;
import static java.util.zip.ZipFile.ENDHDR;
import static java.util.zip.ZipFile.ENDOFF;
import static java.util.zip.ZipFile.LOCTIM;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import jdk.test.lib.Utils;
/* @test
* @bug 8184940 8188869
* @summary JDK 9 rejects zip files where the modified day or month is 0
* or otherwise represent an invalid date, such as 1980-02-30 24:60:60
* @author Liam Miller-Cushon
* @library /test/lib
*/
public class ZeroDate {
public static void main(String[] args) throws Exception {
// create a zip file, and read it in as a byte array
Path path = Utils.createTempFile("bad", ".zip");
try {
try (OutputStream os = Files.newOutputStream(path);
ZipOutputStream zos = new ZipOutputStream(os)) {
ZipEntry e = new ZipEntry("x");
zos.putNextEntry(e);
zos.write((int) 'x');
}
int len = (int) Files.size(path);
byte[] data = new byte[len];
try (InputStream is = Files.newInputStream(path)) {
is.read(data);
}
// year, month, day are zero
testDate(data.clone(), 0, LocalDate.of(1979, 11, 30).atStartOfDay());
// only year is zero
testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5).atStartOfDay());
// month is greater than 12
testDate(data.clone(), 0 << 25 | 13 << 21 | 1 << 16, LocalDate.of(1981, 1, 1).atStartOfDay());
// 30th of February
testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16, LocalDate.of(1980, 3, 1).atStartOfDay());
// 30th of February, 24:60:60
testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16 | 24 << 11 | 60 << 5 | 60 >> 1,
LocalDateTime.of(1980, 3, 2, 1, 1, 0));
} finally {
Files.delete(path);
}
}
private static void testDate(byte[] data, int date, LocalDateTime expected) throws IOException {
// set the datetime
int endpos = data.length - ENDHDR;
int cenpos = u16(data, endpos + ENDOFF);
int locpos = u16(data, cenpos + CENOFF);
writeU32(data, cenpos + CENTIM, date);
writeU32(data, locpos + LOCTIM, date);
// ensure that the archive is still readable, and the date is 1979-11-30
Path path = Utils.createTempFile("out", ".zip");
try (OutputStream os = Files.newOutputStream(path)) {
os.write(data);
}
try (ZipFile zf = new ZipFile(path.toFile())) {
ZipEntry ze = zf.entries().nextElement();
Instant actualInstant = ze.getLastModifiedTime().toInstant();
Instant expectedInstant = expected.atZone(ZoneId.systemDefault()).toInstant();
if (!actualInstant.equals(expectedInstant)) {
throw new AssertionError(
String.format("actual: %s, expected: %s", actualInstant, expectedInstant));
}
} finally {
Files.delete(path);
}
}
static int u8(byte[] data, int offset) {
return data[offset] & 0xff;
}
static int u16(byte[] data, int offset) {
return u8(data, offset) + (u8(data, offset + 1) << 8);
}
private static void writeU32(byte[] data, int pos, int value) {
data[pos] = (byte) (value & 0xff);
data[pos + 1] = (byte) ((value >> 8) & 0xff);
data[pos + 2] = (byte) ((value >> 16) & 0xff);
data[pos + 3] = (byte) ((value >> 24) & 0xff);
}
}

View file

@ -0,0 +1,211 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @test
* @bug 8226530 8303891
* @summary Verify that ZipFile reads size fields using the Zip64 extra
* field when only the 'uncompressed size' field has the ZIP64 "magic value" 0xFFFFFFFF
* @compile Zip64SizeTest.java
* @run junit Zip64SizeTest
*/
public class Zip64SizeTest {
// ZIP file to create
private static final Path ZIP_FILE = Path.of("Zip64SizeTest.zip");
// Contents to write to ZIP entries
private static final byte[] CONTENT = "Hello".getBytes(StandardCharsets.UTF_8);
// This opaque tag will be ignored by ZipEntry.setExtra0
private static final int UNKNOWN_TAG = 0x9902;
// Tag used when converting the extra field to a real ZIP64 extra field
private static final short ZIP64_TAG = 0x1;
// Marker value to indicate that the actual value is stored in the ZIP64 extra field
private static final int ZIP64_MAGIC_VALUE = 0xFFFFFFFF;
/**
* Validate that if the 'uncompressed size' of a ZIP CEN header is 0xFFFFFFFF, then the
* actual size is retrieved from the corresponding ZIP64 Extended information field.
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void validateZipEntrySizes() throws IOException {
createZipFile();
System.out.println("Validating Zip Entry Sizes");
try (ZipFile zip = new ZipFile(ZIP_FILE.toFile())) {
ZipEntry ze = zip.getEntry("first");
System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize());
assertEquals(CONTENT.length, ze.getSize());
ze = zip.getEntry("second");
System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize());
assertEquals(CONTENT.length, ze.getSize());
}
}
/**
* Create a ZIP file with a CEN entry where the 'uncompressed size' is stored in
* the ZIP64 field, but the 'compressed size' is in the CEN field. This makes the
* ZIP64 data block 8 bytes long, which triggers the regression described in 8226530.
*
* The CEN entry for the "first" entry will have the following structure:
* (Note the CEN 'Uncompressed Length' being 0xFFFFFFFF and the ZIP64
* 'Uncompressed Size' being 5)
*
* 0081 CENTRAL HEADER #1 02014B50
* 0085 Created Zip Spec 14 '2.0'
* 0086 Created OS 00 'MS-DOS'
* [...] Omitted for brevity
* 0091 CRC F7D18982
* 0095 Compressed Length 00000007
* 0099 Uncompressed Length FFFFFFFF
* [...] Omitted for brevity
* 00AF Filename 'first'
* 00B4 Extra ID #0001 0001 'ZIP64'
* 00B6 Length 0008
* 00B8 Uncompressed Size 0000000000000005
*
* @throws IOException if an error occurs creating the ZIP File
*/
private static void createZipFile() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
// The 'first' entry will store 'uncompressed size' in the Zip64 format
ZipEntry e1 = new ZipEntry("first");
// Make an extra field with the correct size for an 8-byte 'uncompressed size'
// Zip64 field. Temporarily use the 'unknown' tag 0x9902 to make
// ZipEntry.setExtra0 skip parsing this as a Zip64.
// See APPNOTE.TXT, 4.6.1 Third Party Mappings
byte[] opaqueExtra = createBlankExtra((short) UNKNOWN_TAG, (short) Long.BYTES);
e1.setExtra(opaqueExtra);
zos.putNextEntry(e1);
zos.write(CONTENT);
// A second entry, not in Zip64 format
ZipEntry e2 = new ZipEntry("second");
zos.putNextEntry(e2);
zos.write(CONTENT);
}
byte[] zip = baos.toByteArray();
// Update the CEN of 'first' to use the Zip64 format
updateCENHeaderToZip64(zip);
Files.write(ZIP_FILE, zip);
}
/**
* Update the CEN entry of the "first" entry to use ZIP64 format for the
* 'uncompressed size' field. The updated extra field will have the following
* structure:
*
* 00B4 Extra ID #0001 0001 'ZIP64'
* 00B6 Length 0008
* 00B8 Uncompressed Size 0000000000000005
*
* @param zip the ZIP file to update to ZIP64
*/
private static void updateCENHeaderToZip64(byte[] zip) {
ByteBuffer buffer = ByteBuffer.wrap(zip).order(ByteOrder.LITTLE_ENDIAN);
// Find the offset of the first CEN header
int cenOffset = buffer.getInt(zip.length- ZipFile.ENDHDR + ZipFile.ENDOFF);
// Find the offset of the extra field
int nlen = buffer.getShort(cenOffset + ZipFile.CENNAM);
int extraOffset = cenOffset + ZipFile.CENHDR + nlen;
// Change the header ID from 'unknown' to ZIP64
buffer.putShort(extraOffset, ZIP64_TAG);
// Update the 'uncompressed size' ZIP64 value to the actual uncompressed length
int fieldOffset = extraOffset
+ Short.BYTES // TAG
+ Short.BYTES; // data size
buffer.putLong(fieldOffset, CONTENT.length);
// Set the 'uncompressed size' field of the CEN to 0xFFFFFFFF
buffer.putInt(cenOffset + ZipFile.CENLEN, ZIP64_MAGIC_VALUE);
}
/**
* Create an extra field with the given tag and data block size, and a
* blank data block.
* @return an extra field with the specified tag and size
* @param tag the header id of the extra field
* @param blockSize the size of the extra field's data block
*/
private static byte[] createBlankExtra(short tag, short blockSize) {
int size = Short.BYTES // tag
+ Short.BYTES // data block size
+ blockSize; // data block;
byte[] extra = new byte[size];
ByteBuffer.wrap(extra).order(ByteOrder.LITTLE_ENDIAN)
.putShort(0, tag)
.putShort(Short.BYTES, blockSize);
return extra;
}
/**
* Make sure the needed test files do not exist prior to executing the test
* @throws IOException
*/
@BeforeEach
public void setUp() throws IOException {
deleteFiles();
}
/**
* Remove the files created for the test
* @throws IOException
*/
@AfterEach
public void tearDown() throws IOException {
deleteFiles();
}
/**
* Delete the files created for use by the test
* @throws IOException if an error occurs deleting the files
*/
private static void deleteFiles() throws IOException {
Files.deleteIfExists(ZIP_FILE);
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 6907252
* @summary ZipFileInputStream Not Thread-Safe
* @library /test/lib
* @build jdk.test.lib.Platform
* jdk.test.lib.util.FileUtils
* @run main ZipEntryFreeTest
*/
import java.io.*;
import java.nio.file.Paths;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.*;
import jdk.test.lib.util.FileUtils;
public class ZipEntryFreeTest extends Thread {
private static final int NUM_THREADS = 5;
private static final int TEST_ITERATIONS = 5;
private static final String ZIPFILE_NAME = "large.zip";
private static final String ZIPENTRY_NAME = "random.txt";
private static InputStream is = null;
final Timer timer = new Timer();
public static void main(String args[]) throws Exception {
createZipFile();
try {
for (int i = 0; i < TEST_ITERATIONS; i++) {
runTest();
}
} finally {
FileUtils.deleteFileIfExistsWithRetry(Paths.get(ZIPFILE_NAME));
}
}
private static void runTest() throws Exception {
try (ZipFile zf = new ZipFile(new File(ZIPFILE_NAME))) {
is = zf.getInputStream(zf.getEntry(ZIPENTRY_NAME + "_0"));
Thread[] threadArray = new Thread[NUM_THREADS];
for (int i = 0; i < threadArray.length; i++) {
threadArray[i] = new ZipEntryFreeTest();
}
for (int i = 0; i < threadArray.length; i++) {
threadArray[i].start();
}
for (int i = 0; i < threadArray.length; i++) {
threadArray[i].join();
}
}
}
private static void createZipFile() throws Exception {
Random rnd = new Random(1000L);
byte[] contents = new byte[2_000_000];
ZipEntry ze = null;
try (ZipOutputStream zos =
new ZipOutputStream(new FileOutputStream(ZIPFILE_NAME))) {
// uncompressed mode seemed to tickle the crash
zos.setMethod(ZipOutputStream.STORED);
for (int ze_count = 0; ze_count < 10; ze_count++) {
rnd.nextBytes(contents);
ze = createZipEntry(contents, ze_count);
zos.putNextEntry(ze);
zos.write(contents, 0, contents.length);
}
zos.flush();
}
}
private static ZipEntry createZipEntry(byte[] b, int i) {
ZipEntry ze = new ZipEntry(ZIPENTRY_NAME + "_" + i);
ze.setCompressedSize(b.length);
ze.setSize(b.length);
CRC32 crc = new CRC32();
crc.update(b);
ze.setCrc(crc.getValue());
return ze;
}
@Override
public void run() {
try {
int iteration = 0;
TimerTask tt = (new TimerTask() {
@Override
public void run() {
try {
is.close();
} catch (Exception ex) {
ex.printStackTrace(System.out);
}
}
});
timer.schedule(tt, 50);
while (is.read() != -1 && iteration++ < 1_000) { }
} catch (ZipException ze) {
// ZipException now expected instead of ZIP_Read crash
System.out.println(ze);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
timer.cancel();
}
}
}

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
/* @test
* @bug 8246129
* @summary JDK add metadata to zip files with entries timestamped at the
* lower bound of the DOS time epoch, i.e., 1980-01-01T00:00:00Z
* @run junit/othervm ZipEntryTimeBounds
*/
public class ZipEntryTimeBounds {
@Test
public void testFilesWithEntryAtLowerTimeBoundAreEqual() throws Exception {
// Ensure that entries that end up being exactly at the start of the
// DOS epoch, java.util.zip.ZipEntry.DOSTIME_BEFORE_1980, or
// 1980-01-01 00:00:00 are written without any extra timestamp metadata
// being written
TimeZone.setDefault(TimeZone.getTimeZone("GMT-01"));
File f1 = createTempFile();
makeZip(f1, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 0).getTimeInMillis());
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
File f2 = createTempFile();
makeZip(f2, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 0).getTimeInMillis());
assertEquals(-1L, Files.mismatch(f1.toPath(), f2.toPath()));
TimeZone.setDefault(TimeZone.getTimeZone("GMT+01"));
File f3 = createTempFile();
makeZip(f3, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 0).getTimeInMillis());
assertEquals(-1L, Files.mismatch(f1.toPath(), f3.toPath()));
// Check that the milliseconds part of the time is exactly preserved
assertEquals(0, new ZipFile(f1).getEntry("entry.txt").getTime() % 60000);
assertEquals(0, new ZipFile(f2).getEntry("entry.txt").getTime() % 60000);
assertEquals(0, new ZipFile(f3).getEntry("entry.txt").getTime() % 60000);
}
@Test
public void testFilesWithEntryAfterLowerTimeBoundAreEqual() throws Exception {
// Ensure files written using different timezone with entries set
// shortly after 1980-01-01 00:00:00 produce exactly identical files
// without metadata
TimeZone.setDefault(TimeZone.getTimeZone("GMT-01"));
File f1 = createTempFile();
makeZip(f1, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 1).getTimeInMillis());
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
File f2 = createTempFile();
makeZip(f2, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 1).getTimeInMillis());
assertEquals(-1L, Files.mismatch(f1.toPath(), f2.toPath()));
TimeZone.setDefault(TimeZone.getTimeZone("GMT+01"));
File f3 = createTempFile();
makeZip(f3, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 1).getTimeInMillis() + 999);
assertEquals(-1L, Files.mismatch(f1.toPath(), f3.toPath()));
// Check that the seconds part of the time is lossily preserved,
// rounding down to the previous 2s step since epoch
assertEquals(0, new ZipFile(f1).getEntry("entry.txt").getTime() % 60000);
assertEquals(0, new ZipFile(f2).getEntry("entry.txt").getTime() % 60000);
assertEquals(0, new ZipFile(f3).getEntry("entry.txt").getTime() % 60000);
File f4 = createTempFile();
makeZip(f4, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 2).getTimeInMillis());
assertEquals(2000, new ZipFile(f4).getEntry("entry.txt").getTime() % 60000);
}
@Test
public void testFilesWithEntryBeforeLowerTimeBoundAreNotEqual() throws Exception {
// Files written using different timezone with entries set shortly
// before 1980-01-01 00:00:00 will produce files which add timestamp
// metadata that make the files turn up not equal
TimeZone.setDefault(TimeZone.getTimeZone("GMT-01"));
File f1 = createTempFile();
makeZip(f1, new GregorianCalendar(1979, Calendar.DECEMBER, 31, 23, 59, 59).getTimeInMillis());
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
File f2 = createTempFile();
makeZip(f2, new GregorianCalendar(1979, Calendar.DECEMBER, 31, 23, 59, 59).getTimeInMillis());
assertNotEquals(-1L, Files.mismatch(f1.toPath(), f2.toPath()));
TimeZone.setDefault(TimeZone.getTimeZone("GMT+01"));
File f3 = createTempFile();
makeZip(f3, new GregorianCalendar(1979, Calendar.DECEMBER, 31, 23, 59, 59).getTimeInMillis() + 500);
assertNotEquals(-1L, Files.mismatch(f1.toPath(), f3.toPath()));
// Check that the time is preserved at second precision, no rounding
// to 2s
assertEquals(59000, new ZipFile(f1).getEntry("entry.txt").getTime() % 60000);
assertEquals(59000, new ZipFile(f2).getEntry("entry.txt").getTime() % 60000);
// Milliseconds are discarded even when storing entries with extended
// time metadata
assertEquals(59000, new ZipFile(f3).getEntry("entry.txt").getTime() % 60000);
}
private static void makeZip(File f, long time) throws Exception {
try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f))) {
ZipEntry e = new ZipEntry("entry.txt");
e.setTime(time);
out.putNextEntry(e);
out.write(new byte[] { 0, 1, 2, 3 });
out.closeEntry();
}
}
private static File createTempFile() throws IOException {
File file = File.createTempFile("out", "zip");
file.deleteOnExit();
return file;
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.junit.jupiter.api.Test;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
/*
* @test
* @bug 8355975
* @summary verify that the internal ZIP structure caching in java.util.zip.ZipFile
* uses the correct Charset when parsing the ZIP structure of a ZIP file
* @run junit ZipFileCharsetTest
*/
public class ZipFileCharsetTest {
private static final String ISO_8859_15_NAME = "ISO-8859-15";
/**
* The internal implementation of java.util.zip.ZipFile maintains a cache
* of the ZIP structure of each ZIP file that's currently open. This cache
* helps prevent repeat parsing of the ZIP structure of the same underlying
* ZIP file, every time a ZipFile instance is created for the same ZIP file.
* The cache uses an internal key to map a ZIP file to the corresponding
* ZIP structure that's cached.
* A ZipFile can be constructed by passing a Charset which will be used to
* decode the entry names (and comment) in a ZIP file.
* The test verifies that when multiple ZipFile instances are
* constructed using different Charsets but the same underlying ZIP file,
* then the internal caching implementation of ZipFile doesn't end up using
* a wrong Charset for parsing the ZIP structure of the ZIP file.
*/
@Test
void testCachedZipFileSource() throws Exception {
// ISO-8859-15 is not a standard charset in Java. We skip this test
// when it is unavailable
assumeTrue(Charset.availableCharsets().containsKey(ISO_8859_15_NAME),
"skipping test since " + ISO_8859_15_NAME + " charset isn't available");
// We choose the byte 0xA4 for entry name in the ZIP file.
// 0xA4 is "Euro sign" in ISO-8859-15 charset and
// "Currency sign (generic)" in ISO-8859-1 charset.
final byte[] entryNameBytes = new byte[]{(byte) 0xA4}; // intentional cast
final Charset euroSignCharset = Charset.forName(ISO_8859_15_NAME);
final Charset currencySignCharset = ISO_8859_1;
final String euroSign = new String(entryNameBytes, euroSignCharset);
final String currencySign = new String(entryNameBytes, currencySignCharset);
// create a ZIP file whose entry name is encoded using ISO-8859-15 charset
final Path zip = createZIP("euro", euroSignCharset, entryNameBytes);
// Construct a ZipFile instance using the (incorrect) charset ISO-8859-1.
// While that ZipFile instance is still open (and the ZIP file structure
// still cached), construct another instance for the same ZIP file, using
// the (correct) charset ISO-8859-15.
try (ZipFile incorrect = new ZipFile(zip.toFile(), currencySignCharset);
ZipFile correct = new ZipFile(zip.toFile(), euroSignCharset)) {
// correct encoding should resolve the entry name to euro sign
// and the entry should be thus be located
assertNotNull(correct.getEntry(euroSign), "euro sign entry missing in " + correct);
// correct encoding should not be able to find an entry name
// with the currency sign
assertNull(correct.getEntry(currencySign), "currency sign entry unexpectedly found in "
+ correct);
// incorrect encoding should resolve the entry name to currency sign
// and the entry should be thus be located by the currency sign name
assertNotNull(incorrect.getEntry(currencySign), "currency sign entry missing in "
+ incorrect);
// incorrect encoding should not be able to find an entry name
// with the euro sign
assertNull(incorrect.getEntry(euroSign), "euro sign entry unexpectedly found in "
+ incorrect);
}
}
/**
* Creates and return ZIP file whose entry names are encoded using the given {@code charset}
*/
private static Path createZIP(final String fileNamePrefix, final Charset charset,
final byte[] entryNameBytes) throws IOException {
final Path zip = Files.createTempFile(Path.of("."), fileNamePrefix, ".zip");
// create a ZIP file whose entry name(s) use the given charset
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zip), charset)) {
zos.putNextEntry(new ZipEntry(new String(entryNameBytes, charset)));
final byte[] entryContent = "doesnotmatter".getBytes(US_ASCII);
zos.write(entryContent);
zos.closeEntry();
}
return zip;
}
}

View file

@ -0,0 +1,587 @@
/*
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Formatter;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @test
* @bug 8276123
* @summary ZipFile::getEntry will not return a file entry when there is a
* directory entry of the same name within a Zip File
* @run junit/othervm ZipFileDuplicateEntryTest
*/
public class ZipFileDuplicateEntryTest {
/**
* Name to use for creating Zip entries
*/
private static final String ENTRY_NAME = "entry";
/**
* Zip and Jar files to be created
*/
private static final Path ZIP_FILE = Paths.get("fileDirEntry.zip");
private static final Path ZIP_FILE2 = Paths.get("OnlyDirEntry.zip");
private static final Path DUPLICATE_FILE_ENTRY_FILE = Paths.get("DupFIleEntry.zip");
private static final Path TEST_JAR = Paths.get("fileDirEntry.jar");
/**
* Directory entry added to the Zip File.
*/
private static final Entry DIR_ENTRY =
Entry.of(ENTRY_NAME + "/", ZipEntry.DEFLATED,
"I am a Directory");
/**
* File entry added to the Zip File.
*/
private static final Entry FILE_ENTRY =
Entry.of(ENTRY_NAME, ZipEntry.DEFLATED, "I am a File");
/**
* Duplicate File entry added to the Zip file. This is the 2nd entry added
* to the Zip file and is expected to be returned.
*/
private static final Entry DUPLICATE_FILE_ENTRY =
Entry.of(ENTRY_NAME, ZipEntry.DEFLATED, "Yet another File");
/**
* Entries expected to be returned via ZipFile::stream
*/
private static final List<String> EXPECTED_ENTRIES =
Arrays.asList(FILE_ENTRY.name, DIR_ENTRY.name);
/**
* Max buffer size for readAllBytes method which can be used when
* InputStream::readAllBytes is not available
*/
private static final int MAX_BUFFER_SIZE = 1024;
/**
* Flag to enable test output
*/
private static final boolean DEBUG = false;
/**
* Array representing a Jar File with the entries:
* Name: entry, contents: "I am a File"
* Name: entry, contents: "Yet another File"
* See createByteArray()
*/
private static final byte[] DUPLICATE_ENTRY_JAR_BYTES = {
(byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x8,
(byte) 0x8, (byte) 0x0, (byte) 0x60, (byte) 0x59, (byte) 0x55, (byte) 0x53, (byte) 0x8e, (byte) 0x39,
(byte) 0x14, (byte) 0x49, (byte) 0xd, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xb, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x65, (byte) 0x6e,
(byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x1, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0xb,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xd,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xf3,
(byte) 0x54, (byte) 0x48, (byte) 0xcc, (byte) 0x55, (byte) 0x48, (byte) 0x54, (byte) 0x70, (byte) 0xcb,
(byte) 0xcc, (byte) 0x49, (byte) 0x5, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4,
(byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x60, (byte) 0x59,
(byte) 0x55, (byte) 0x53, (byte) 0xe1, (byte) 0x4c, (byte) 0x29, (byte) 0xa4, (byte) 0x12, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5, (byte) 0x0,
(byte) 0x14, (byte) 0x0, (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x1,
(byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x12, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x8b, (byte) 0x4c, (byte) 0x2d, (byte) 0x51, (byte) 0x48,
(byte) 0xcc, (byte) 0xcb, (byte) 0x2f, (byte) 0xc9, (byte) 0x48, (byte) 0x2d, (byte) 0x52, (byte) 0x70,
(byte) 0xcb, (byte) 0xcc, (byte) 0x49, (byte) 0x5, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1,
(byte) 0x2, (byte) 0x14, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8,
(byte) 0x0, (byte) 0x60, (byte) 0x59, (byte) 0x55, (byte) 0x53, (byte) 0x8e, (byte) 0x39, (byte) 0x14,
(byte) 0x49, (byte) 0xd, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xb, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x72, (byte) 0x79,
(byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, (byte) 0x0, (byte) 0x14, (byte) 0x0,
(byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x60, (byte) 0x59, (byte) 0x55, (byte) 0x53,
(byte) 0xe1, (byte) 0x4c, (byte) 0x29, (byte) 0xa4, (byte) 0x12, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x44, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x65, (byte) 0x6e,
(byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2, (byte) 0x0, (byte) 0x2, (byte) 0x0, (byte) 0x66,
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x8d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0,
};
/**
* Create Zip files used by the tests.
*
* @throws IOException If an error occurs
*/
@BeforeAll
public static void setup() throws IOException {
/**
* Zip contains two entries named "entry" and "entry/"
*/
Files.deleteIfExists(ZIP_FILE);
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(ZIP_FILE))) {
zos.putNextEntry(new ZipEntry(FILE_ENTRY.name));
zos.write(FILE_ENTRY.bytes);
zos.closeEntry();
zos.putNextEntry(new ZipEntry(DIR_ENTRY.name));
zos.write(DIR_ENTRY.bytes);
zos.closeEntry();
}
/**
* Jar contains two entries named "entry" and "entry/"
*/
Files.deleteIfExists(TEST_JAR);
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(TEST_JAR))) {
jos.putNextEntry(new JarEntry(FILE_ENTRY.name));
jos.write(FILE_ENTRY.bytes);
jos.closeEntry();
jos.putNextEntry(new JarEntry(DIR_ENTRY.name));
jos.write(DIR_ENTRY.bytes);
jos.closeEntry();
}
/**
* Zip contains the entry "entry/"
*/
Files.deleteIfExists(ZIP_FILE2);
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(ZIP_FILE2))) {
zos.putNextEntry(new ZipEntry(DIR_ENTRY.name));
zos.write(DIR_ENTRY.bytes);
zos.closeEntry();
}
/**
* Create a Jar that contains two entries named "entry"
*/
Files.deleteIfExists(DUPLICATE_FILE_ENTRY_FILE);
Files.write(DUPLICATE_FILE_ENTRY_FILE, DUPLICATE_ENTRY_JAR_BYTES);
}
/**
* Clean up after the test run
*
* @throws IOException If an error occurs
*/
@AfterAll
public static void cleanup() throws IOException {
Files.deleteIfExists(ZIP_FILE);
Files.deleteIfExists(ZIP_FILE2);
Files.deleteIfExists(DUPLICATE_FILE_ENTRY_FILE);
Files.deleteIfExists(TEST_JAR);
}
/**
* MethodSource used to specify the Zip entries to use
*
* @return Stream that indicates which Entry to use within the test
*/
public static Stream<Entry> entries() {
return Stream.of(FILE_ENTRY, DIR_ENTRY);
}
/**
* Test whether ZipFile::getEntry can find a directory entry within a Zip
* file specifying "name" vs "name/"
*
* @throws IOException If an error occurs
*/
@Test
public void readDirWithoutSlash() throws IOException {
System.out.printf("%n%n**** readDirWithoutSlash ***%n");
try (ZipFile zip = new ZipFile(ZIP_FILE2.toString())) {
ZipEntry ze = zip.getEntry(ENTRY_NAME);
if (DEBUG) {
System.out.printf(" Entry:%s, found:%s%n", ENTRY_NAME, ze != null);
}
assertNotNull(ze);
assertTrue(ze.isDirectory());
try (InputStream in = zip.getInputStream(ze)) {
byte[] bytes = in.readAllBytes();
if (DEBUG) {
System.out.printf("name: %s, isDirectory: %s, payload= %s%n",
ze.getName(), ze.isDirectory(), new String(bytes));
}
assertArrayEquals(DIR_ENTRY.bytes, bytes,
String.format("Expected payload: %s",
new String(DIR_ENTRY.bytes)));
}
}
}
/**
* Validate that ZipFile::getEntry will return the correct entry when a file
* and directory have the same name
*
* @param entry The entry to search for
* @throws IOException If an error occurs
*/
@ParameterizedTest
@MethodSource("entries")
public void testSameFileDirEntryName(Entry entry) throws IOException {
System.out.printf("%n%n**** testSameFileDirEntryName ***%n");
try (ZipFile zip = new ZipFile(ZIP_FILE.toString())) {
ZipEntry ze = zip.getEntry(entry.name);
if (DEBUG) {
System.out.printf(" Entry:%s, found:%s%n", entry.name, ze != null);
}
assertNotNull(ze);
try (InputStream in = zip.getInputStream(ze)) {
byte[] bytes = in.readAllBytes();
if (DEBUG) {
System.out.printf("name: %s, isDirectory: %s, payload= %s%n",
ze.getName(), ze.isDirectory(), new String(bytes));
}
assertArrayEquals(entry.bytes, bytes,
String.format("Expected payload: %s", new String(entry.bytes)));
}
}
}
/**
* Validate that ZipFile::getEntry will return the correct entry, which
* is the second entry, when there are duplicate entries within the Zip file.
*
* @throws IOException If an error occurs
*/
@Test
public void DupFileEntryTest() throws IOException {
System.out.printf("%n%n**** DupFileEntryTest ***%n");
try (ZipFile zip =
new ZipFile(DUPLICATE_FILE_ENTRY_FILE.toString())) {
ZipEntry ze = zip.getEntry(ENTRY_NAME);
if (DEBUG) {
System.out.printf(" Entry:%s, found:%s%n", ENTRY_NAME, ze != null);
}
assertNotNull(ze);
try (InputStream in = zip.getInputStream(ze)) {
byte[] bytes = in.readAllBytes();
if (DEBUG) {
System.out.printf("name: %s, isDirectory: %s, payload= %s%n",
ze.getName(), ze.isDirectory(), new String(bytes));
}
assertArrayEquals(DUPLICATE_FILE_ENTRY.bytes, bytes,
String.format("Expected payload: %s", new String(DUPLICATE_FILE_ENTRY.bytes)));
}
}
}
/**
* Verify that ZipInputStream can be used to read all Zip entries including
* a file and directory entry with the same name
*
* @throws IOException If an error occurs
*/
@Test
public void ZipInputStreamTest() throws IOException {
System.out.printf("%n%n**** ZipInputStreamTest ***%n");
try (ZipInputStream zis = new ZipInputStream(
new FileInputStream(ZIP_FILE.toFile()))) {
ZipEntry zipEntry = zis.getNextEntry();
assertNotNull(zipEntry);
while (zipEntry != null) {
Entry e;
if (zipEntry.getName().equals(FILE_ENTRY.name)) {
e = FILE_ENTRY;
} else if (zipEntry.getName().equals(DIR_ENTRY.name)) {
e = DIR_ENTRY;
} else {
throw new RuntimeException(
String.format("Invalid Zip entry: %s", zipEntry.getName()));
}
assertEquals(e.method, zipEntry.getMethod());
assertArrayEquals(e.bytes, zis.readAllBytes(),
String.format("Expected payload: %s", new String(e.bytes)));
zipEntry = zis.getNextEntry();
}
}
}
/**
* Verify that ZipFile::stream returns all Zip entries including
* a file and directory entry with the same name
*
* @throws IOException If an error occurs
*/
@Test
public void ZipFileStreamTest() throws IOException {
System.out.printf("%n%n**** ZipFileStreamTest ***%n");
try (ZipFile zf = new ZipFile(ZIP_FILE.toFile())) {
List<? extends ZipEntry> entries = zf.stream().collect(Collectors.toList());
assertEquals(EXPECTED_ENTRIES.size(), entries.size());
for (ZipEntry e : entries) {
assertTrue(EXPECTED_ENTRIES.contains(e.getName()));
}
}
}
/**
* Verify that JarFile can be used to read all the entries including
* a file and directory entry with the same name
*
* @param entry The entry to validate
* @throws IOException If an error occurs
*/
@ParameterizedTest
@MethodSource("entries")
public void JarFileInputStreamTest(Entry entry) throws IOException {
System.out.printf("%n%n**** JarFileInputStreamTest ***%n");
try (JarFile jarFile = new JarFile(TEST_JAR.toFile())) {
JarEntry je = jarFile.getJarEntry(entry.name);
assertNotNull(je);
if (DEBUG) {
System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n",
entry.name, je.getMethod(), entry.method);
}
assertEquals(entry.method, je.getMethod());
try (InputStream in = jarFile.getInputStream(je)) {
byte[] bytes = in.readAllBytes();
if (DEBUG) {
System.out.printf("bytes= %s, expected=%s%n",
new String(bytes), new String(entry.bytes));
}
assertArrayEquals(entry.bytes, bytes,
String.format("Expected payload: %s", new String(entry.bytes)));
}
}
}
/**
* Verify that JarInputStream can be used to read all entries including
* a file and directory entry with the same name
*
* @throws IOException If an error occurs
*/
@Test
public void JarInputStreamTest() throws IOException {
System.out.printf("%n%n**** JarInputStreamTest ***%n");
try (JarInputStream jis = new JarInputStream(
new FileInputStream(TEST_JAR.toFile()))) {
JarEntry jarEntry = jis.getNextJarEntry();
assertNotNull(jarEntry);
while (jarEntry != null) {
Entry e;
if (jarEntry.getName().equals(FILE_ENTRY.name)) {
e = FILE_ENTRY;
} else if (jarEntry.getName().equals(DIR_ENTRY.name)) {
e = DIR_ENTRY;
} else {
throw new RuntimeException(
String.format("Invalid Jar entry: %s", jarEntry.getName()));
}
assertEquals(e.method, jarEntry.getMethod());
assertArrayEquals(e.bytes, jis.readAllBytes(),
String.format("Expected payload: %s", new String(e.bytes)));
jarEntry = jis.getNextJarEntry();
}
}
}
/**
* Verify that JarURLConnection can be used to access all the entries including
* a file and directory entry with the same name within a jar file
*
* @param entry The entry to validate
* @throws IOException If an error occurs
*/
@ParameterizedTest
@MethodSource("entries")
public void JarURLConnectionTest(Entry entry) throws Exception {
System.out.printf("%n%n**** JarURLConnectionTest ***%n");
URL url = new URL("jar:" + TEST_JAR.toUri().toURL() + "!/" + entry.name);
if (DEBUG) {
System.out.printf("URL=%s%n", url);
}
JarURLConnection con = (JarURLConnection) url.openConnection();
con.connect();
JarEntry je = con.getJarEntry();
try (JarFile jarFile = con.getJarFile()) {
assertNotNull(je);
assertNotNull(jarFile);
assertNull(con.getAttributes());
assertNull(con.getMainAttributes());
assertNull(con.getManifest());
assertEquals(entry.name, je.getName());
assertEquals(entry.name, con.getEntryName());
assertEquals(entry.method, je.getMethod());
assertEquals(TEST_JAR.toUri().toURL(), con.getJarFileURL());
if (DEBUG) {
System.out.printf(" getEntryName: %s, getJarFileURL:%s%n",
con.getEntryName(), con.getJarFileURL());
System.out.printf(" Jar Entry= %s, size= %s%n", je.getName(), je.getSize());
}
try (InputStream is = jarFile.getInputStream(je)) {
byte[] bytes = is.readAllBytes();
if (DEBUG) {
System.out.printf(" Bytes read:%s%n", new String(bytes));
}
assertArrayEquals(entry.bytes, bytes,
String.format("Expected payload: %s", new String(entry.bytes)));
}
}
}
/**
* Verify that JarFile::stream returns all entries including
* a file and directory entry with the same name
*
* @throws IOException If an error occurs
*/
@Test
public void JarFileStreamTest() throws IOException {
System.out.printf("%n%n**** JarFileStreamTest ***%n");
try (JarFile jf = new JarFile(TEST_JAR.toFile())) {
List<? extends JarEntry> entries = jf.stream().collect(Collectors.toList());
assertEquals(EXPECTED_ENTRIES.size(), jf.size());
for (JarEntry e : entries) {
assertTrue(EXPECTED_ENTRIES.contains(e.getName()));
}
}
}
/**
* Method used to read the bytes from an InputStream. This method is
* here so that the test could be backported to JDK 8 if needed as
* InputStream::readAllBytes() does not exist
*
* @param is The InputStream to read from
* @return The byte array representing bytes read from the InputStream
* @throws IOException If an error occurs
*/
public static byte[] readAllBytes(InputStream is) throws IOException {
byte[] data = new byte[MAX_BUFFER_SIZE];
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int len;
while ((len = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, len);
}
buffer.flush();
return buffer.toByteArray();
}
/**
* Method used to create a byte[] representing a Jar file with
* duplicate file entries. This uses ZipArchiveOutputStream as ZipOutputStream
* will fail with a "java.util.zip.ZipException: duplicate entry".
*/
// public static void createJarWithDuplicateFileEntries() throws IOException {
// Files.deleteIfExists(DUPFILE_ENTRY_FILE);
// try (ZipArchiveOutputStream zos =
// new ZipArchiveOutputStream(DUPFILE_ENTRY_FILE.toFile())) {
// zos.putArchiveEntry(new ZipArchiveEntry(FILE_ENTRY.name));
// zos.write(FILE_ENTRY.bytes);
// zos.putArchiveEntry(new ZipArchiveEntry(FILE_ENTRY.name));
// zos.write("Yet another File".getBytes(StandardCharsets.UTF_8));
// zos.closeArchiveEntry();
// } catch (IOException e) {
// e.printStackTrace();
// }
// byte[] jarBytes = Files.readAllBytes(DUPFILE_ENTRY_FILE);
// String result = createByteArray(jarBytes, "DUPLICATE_ENTRY_JAR_BYTES");
// System.out.println(result);
// }
/**
* Utility method which takes a byte array and converts to byte array
* declaration. For example:
* <pre>
* {@code
* var fooJar = Files.readAllBytes(Path.of("foo.jar"));
* var result = createByteArray(fooJar, "FOOBYTES");
* }
* </pre>
*
* @param bytes A byte array used to create a byte array declaration
* @param name Name to be used in the byte array declaration
* @return The formatted byte array declaration
*/
public static String createByteArray(byte[] bytes, String name) {
StringBuilder sb = new StringBuilder(bytes.length * 5);
Formatter fmt = new Formatter(sb);
fmt.format(" public static byte[] %s = {", name);
final int linelen = 8;
for (int i = 0; i < bytes.length; i++) {
if (i % linelen == 0) {
fmt.format("%n ");
}
fmt.format(" (byte) 0x%x,", bytes[i] & 0xff);
}
fmt.format("%n };%n");
return sb.toString();
}
/**
* Represents an entry in a Zip file. An entry encapsulates a name, a
* compression method, and its contents/data.
*/
public static class Entry {
public final String name;
public final int method;
public final byte[] bytes;
public Entry(String name, int method, String contents) {
this.name = name;
this.method = method;
this.bytes = contents.getBytes(StandardCharsets.UTF_8);
}
public static Entry of(String name, int method, String contents) {
return new Entry(name, method, contents);
}
}
}

View file

@ -0,0 +1,294 @@
/*
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @test
* @bug 8231451
* @summary Basic tests for ZipFileInputStream::skip
* @modules jdk.zipfs
* @run junit/othervm ZipFileInputStreamSkipTest
*/
public class ZipFileInputStreamSkipTest {
// Stored and Deflated Zip File paths used by the tests
private static final Path STORED_ZIPFILE = Path.of("skipStoredEntries.zip");
private static final Path DEFLATED_ZIPFILE = Path.of("skipDeflatedEntries.zip");
// Saved Entries added to the relevant Zip file
private static final HashMap<String, Entry> STORED_ZIP_ENTRIES = new HashMap<>();
private static final HashMap<String, Entry> DEFLATED_ZIP_ENTRIES = new HashMap<>();
/**
* Create the Zip Files used by the tests
*
* @throws IOException If an error occurs creating the Zip Files
*/
@BeforeAll
static void createZip() throws IOException {
Entry e0 = Entry.of("Entry-0", ZipEntry.STORED, "Tennis Pro");
Entry e1 = Entry.of("Entry-1", ZipEntry.STORED,
"United States Tennis Association");
Entry e2 = Entry.of("Entry-2", ZipEntry.DEFLATED, "Cardio Tennis");
Entry e3 = Entry.of("Entry-3", ZipEntry.DEFLATED, "USTA League Championships");
// Add entries
STORED_ZIP_ENTRIES.put(e0.name, e0);
STORED_ZIP_ENTRIES.put(e1.name, e1);
DEFLATED_ZIP_ENTRIES.put(e2.name, e2);
DEFLATED_ZIP_ENTRIES.put(e3.name, e3);
Files.deleteIfExists(STORED_ZIPFILE);
Files.deleteIfExists(DEFLATED_ZIPFILE);
createZipFile(STORED_ZIPFILE,
Map.of("create", "true", "noCompression", "true"),
e0, e1);
createZipFile(DEFLATED_ZIPFILE, Map.of("create", "true"), e2, e3);
}
/**
* Delete Zip Files created for the test
*
* @throws IOException If an error occurs during cleanup
*/
@AfterAll
static void cleanUp() throws IOException {
Files.deleteIfExists(STORED_ZIPFILE);
Files.deleteIfExists(DEFLATED_ZIPFILE);
}
/**
* Validate that you can skip forward within a STORED entry
* and then read the expected data for the entry
*
* @throws Exception If an error occurs during the test
*/
@Test
public void testStoredSkip() throws Exception {
try (ZipFile zf = new ZipFile(STORED_ZIPFILE.toFile())) {
var entries = zf.entries();
while (entries.hasMoreElements()) {
var entry = entries.nextElement();
var entrySize = entry.getSize();
long midpoint = entrySize / 2;
Entry expected = STORED_ZIP_ENTRIES.get(entry.getName());
assertNotNull(expected);
try (InputStream in = zf.getInputStream(entry)) {
// Check that if we specify 0, that we return the correct
// skip value value
assertEquals(0, in.skip(0));
// Try to skip past EOF and should return remaining bytes
assertEquals(entrySize, in.skip(entrySize + 100));
// Return to BOF and then specify a value which would
// overflow the projected skip value and return the
// number of bytes moved to reach EOF
assertEquals(-entrySize, in.skip(-entrySize));
assertEquals(entrySize, in.skip(Long.MAX_VALUE));
// From midpoint, try to skip past EOF and then skip back
// to BOF
assertEquals(-entrySize, in.skip(-entrySize));
assertEquals(midpoint, in.skip(midpoint));
assertEquals(entrySize - midpoint, in.skip(1000));
assertEquals(-entrySize, in.skip(-entrySize));
// Read remaining bytes and validate against expected bytes
byte[] bytes = in.readAllBytes();
assertArrayEquals(expected.bytes, bytes);
assertEquals(expected.bytes.length, bytes.length);
}
}
}
}
/**
* Validate that you can skip backwards within a STORED entry
* and then read the expected data for the entry
*
* @throws Exception If an error occurs during the test
*/
@Test
public void testStoredNegativeSkip() throws Exception {
try (ZipFile zf = new ZipFile(STORED_ZIPFILE.toFile())) {
var entries = zf.entries();
while (entries.hasMoreElements()) {
var entry = entries.nextElement();
var entrySize = entry.getSize();
var midpoint = entrySize / 2;
Entry expected = STORED_ZIP_ENTRIES.get(entry.getName());
assertNotNull(expected);
try (InputStream in = zf.getInputStream(entry)) {
// Check that if you try to move past BOF
// that we return the correct value
assertEquals(0, in.skip(-1));
assertEquals(0, in.skip(-100));
assertEquals(0, in.skip(Long.MIN_VALUE));
// Go to midpoint in file; then specify a value before
// BOF which should result in the number of
// bytes to BOF returned
assertEquals(midpoint, in.skip(midpoint));
assertEquals(-midpoint, in.skip(-(midpoint + 10)));
// From midpoint, move back a couple of bytes
assertEquals(midpoint, in.skip(midpoint));
assertEquals(-2, in.skip(-2));
// Read the remaining bytes and compare to the expected bytes
byte[] bytes = in.readAllBytes();
assertArrayEquals(Arrays.copyOfRange(expected.bytes,
(int)midpoint - 2, (int) entrySize), bytes);
assertEquals(entrySize - midpoint + 2, bytes.length);
}
}
}
}
/**
* Validate that you can skip forward within a DEFLATED entry
* and then read the expected data for the entry
*
* @throws Exception If an error occurs during the test
*/
@Test
public void testDeflatedSkip() throws Exception {
try (ZipFile zf = new ZipFile(DEFLATED_ZIPFILE.toFile())) {
var toSkip = 5; // Bytes to Skip
var entries = zf.entries();
while (entries.hasMoreElements()) {
var entry = entries.nextElement();
Entry expected = DEFLATED_ZIP_ENTRIES.get(entry.getName());
assertNotNull(expected);
try (InputStream in = zf.getInputStream(entry)) {
assertEquals(toSkip, in.skip(toSkip));
byte[] bytes = in.readAllBytes();
var ebytes = Arrays.copyOfRange(expected.bytes,
toSkip, expected.bytes.length);
assertArrayEquals(ebytes, bytes);
assertEquals(expected.bytes.length - toSkip, bytes.length);
}
}
}
}
/**
* Validate that an IllegalArgumentException is thrown if you specify
* a negative skip value for a DEFLATED entry.
*
* @throws Exception If an unexpected error occurs during the test
*/
@Test
public void testDeflatedIOException() throws Exception {
try (ZipFile zf = new ZipFile(DEFLATED_ZIPFILE.toFile())) {
var entries = zf.entries();
while (entries.hasMoreElements()) {
var entry = entries.nextElement();
assertNotNull(entry);
try (InputStream in = zf.getInputStream(entry)) {
// Cannot specify a negative value
assertThrows(IllegalArgumentException.class, () -> in.skip((-1)));
}
}
}
}
/**
* Create a Zip File System using the specified properties and a Zip file
* with the specified number of entries
*
* @param zipFile Path to the Zip File to create
* @param env Properties used for creating the Zip Filesystem
* @param entries The entries to add to the Zip File
* @throws IOException If an error occurs while creating the Zip file
*/
private static void createZipFile(Path zipFile, Map<String, String> env,
Entry... entries) throws IOException {
try (FileSystem zipfs =
FileSystems.newFileSystem(zipFile, env)) {
for (Entry e : entries) {
Files.writeString(zipfs.getPath(e.name), new String(e.bytes));
}
}
}
/**
* Represents an entry in a Zip file. An entry encapsulates a name, a
* compression method, and its contents/data.
*/
static class Entry {
private final String name;
private final int method;
private final byte[] bytes;
Entry(String name, int method, String contents) {
this.name = name;
this.method = method;
this.bytes = contents.getBytes(StandardCharsets.UTF_8);
}
static Entry of(String name, int method, String contents) {
return new Entry(name, method, contents);
}
/**
* Returns a new Entry with the same name and compression method as this
* Entry but with the given content.
*/
Entry content(String contents) {
return new Entry(name, method, contents);
}
}
}

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static java.nio.charset.StandardCharsets.US_ASCII;
/*
* @test
* @bug 8347712
* @summary verify that different instances of java.util.zip.ZipFile do not share
* the same instance of (non-thread-safe) java.nio.charset.CharsetEncoder/CharsetDecoder
* @run junit ZipFileSharedSourceTest
*/
public class ZipFileSharedSourceTest {
static Path createZipFile(final Charset charset) throws Exception {
final Path zipFilePath = Files.createTempFile(Path.of("."), "8347712", ".zip");
try (OutputStream os = Files.newOutputStream(zipFilePath);
ZipOutputStream zos = new ZipOutputStream(os, charset)) {
final int numEntries = 10240;
for (int i = 1; i <= numEntries; i++) {
final ZipEntry entry = new ZipEntry("entry-" + i);
zos.putNextEntry(entry);
zos.write("foo bar".getBytes(US_ASCII));
zos.closeEntry();
}
}
return zipFilePath;
}
static List<Arguments> charsets() {
return List.of(
Arguments.of(StandardCharsets.UTF_8),
Arguments.of(StandardCharsets.ISO_8859_1),
Arguments.of(US_ASCII)
);
}
/**
* In this test, multiple concurrent threads each create an instance of java.util.zip.ZipFile
* with the given {@code charset} for the same underlying ZIP file. Each of the threads
* then iterate over the entries of their ZipFile instance. The test verifies that such access,
* where each thread is accessing an independent ZipFile instance corresponding to the same
* underlying ZIP file, doesn't lead to unexpected failures contributed by concurrent
* threads.
*/
@ParameterizedTest
@MethodSource("charsets")
void testMultipleZipFileInstances(final Charset charset) throws Exception {
final Path zipFilePath = createZipFile(charset);
final int numTasks = 200;
final CountDownLatch startLatch = new CountDownLatch(numTasks);
final List<Future<Void>> results = new ArrayList<>();
try (final ExecutorService executor =
Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory())) {
for (int i = 0; i < numTasks; i++) {
final var task = new ZipEntryIteratingTask(zipFilePath, charset,
startLatch);
results.add(executor.submit(task));
}
System.out.println(numTasks + " tasks submitted, waiting for them to complete");
for (final Future<Void> f : results) {
f.get();
}
}
System.out.println("All " + numTasks + " tasks completed successfully");
}
private static final class ZipEntryIteratingTask implements Callable<Void> {
private final Path file;
private final Charset charset;
private final CountDownLatch startLatch;
private ZipEntryIteratingTask(final Path file, final Charset charset,
final CountDownLatch startLatch) {
this.file = file;
this.charset = charset;
this.startLatch = startLatch;
}
@Override
public Void call() throws Exception {
// let other tasks know we are ready to run
this.startLatch.countDown();
// wait for other tasks to be ready to run
this.startLatch.await();
// create a new instance of ZipFile and iterate over the entries
try (final ZipFile zf = new ZipFile(this.file.toFile(), this.charset)) {
final var entries = zf.entries();
while (entries.hasMoreElements()) {
final ZipEntry ze = entries.nextElement();
// additionally exercise the ZipFile.getEntry() method
zf.getEntry(ze.getName());
}
}
return null;
}
}
}

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8317678
* @modules java.base/java.util.zip:open
* @summary Fix up hashCode() for ZipFile.Source.Key
* @library /test/lib
* @run junit/othervm ZipSourceCache
*/
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.zip.*;
import jdk.test.lib.util.FileUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.*;
public class ZipSourceCache {
private static final String ZIPFILE_NAME =
System.currentTimeMillis() + "-bug8317678.zip";
private static final String ZIPENTRY_NAME = "random.txt";
private static final String INVALID_LOC_EXCEPTION =
"ZipFile invalid LOC header (bad signature)";
private static final boolean DEBUG = false;
private static File relativeFile = new File(ZIPFILE_NAME);
private static File absoluteFile = new File(ZIPFILE_NAME).getAbsoluteFile();
private static boolean hasfileKeySupport;
@BeforeAll
public static void setup() throws Exception {
createZipFile("test1");
var attrs = Files.readAttributes(relativeFile.toPath(), BasicFileAttributes.class);
hasfileKeySupport = (attrs.fileKey() != null);
}
@AfterAll
public static void cleanup() throws IOException {
FileUtils.deleteFileIfExistsWithRetry(Path.of(ZIPFILE_NAME));
}
/*
* Monitor the internal "files" HashMap to ensure that we only
* create one <Key, Source> mapping per unique zip file.
*
* This test also ensures that a new <Key, Source> mapping is created
* if an update to an existing zip file is detected.
*/
@Test
public void testKeySourceMapping() throws Exception {
ZipFile absoluteZipFile;
HashMap internalMap;
int numSources;
try (ZipFile zipFile = new ZipFile(relativeFile)) {
Class source = Class.forName("java.util.zip.ZipFile$Source");
Field filesMap = source.getDeclaredField("files");
filesMap.setAccessible(true);
internalMap = (HashMap) filesMap.get(zipFile);
numSources = internalMap.size();
// opening of same zip file shouldn't cause new Source
// to be constructed on filesystems which support fileKey()
absoluteZipFile = new ZipFile(absoluteFile);
if (hasfileKeySupport) {
assertEquals(numSources, internalMap.size());
} else {
assertEquals(++numSources, internalMap.size());
}
// update the zip file, should expect a new Source Object
// ignore this part of test if file can't be updated (can't overwrite)
if (createZipFile("differentContent")) {
ZipFile z = new ZipFile(relativeFile);
// update of file should trigger new <Key, Source> mapping
assertEquals(++numSources, internalMap.size());
// new Source created, CEN structure should map fine
readZipFileContents(z);
// the old Source in use for old file, should no longer map correctly
IOException ioe = assertThrows(IOException.class, () -> readZipFileContents(absoluteZipFile));
assertEquals(INVALID_LOC_EXCEPTION, ioe.getMessage());
z.close();
assertEquals(--numSources, internalMap.size());
}
}
// with fileKey() support, the close() call shouldn't remove the
// Source entry just yet since we still have one reference to the file
if (hasfileKeySupport) {
assertEquals(numSources, internalMap.size());
} else {
assertEquals(--numSources, internalMap.size());
}
if (absoluteZipFile != null) {
absoluteZipFile.close();
}
// now, the Source entry key should be removed
assertEquals(--numSources, internalMap.size());
}
private static void readZipFileContents(ZipFile zf) throws IOException {
var e = zf.entries();
while (e.hasMoreElements()) {
InputStream is = zf.getInputStream(e.nextElement());
String s = new String(is.readAllBytes());
if (DEBUG) System.err.println(s);
}
}
private static boolean createZipFile(String content) {
CRC32 crc32 = new CRC32();
long t = System.currentTimeMillis();
// let's have at least 2 entries created to ensure
// that a bad zip structure is detected if file is updated
int numEntries = new Random().nextInt(10) + 2;
File zipFile = new File(ZIPFILE_NAME);
try (FileOutputStream fos = new FileOutputStream(zipFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos)) {
for (int i = 0; i < numEntries; i++) {
ZipEntry e = new ZipEntry(ZIPENTRY_NAME + i);
e.setMethod(ZipEntry.STORED);
byte[] toWrite = content.repeat(i+1).getBytes();
e.setTime(t);
e.setSize(toWrite.length);
crc32.reset();
crc32.update(toWrite);
e.setCrc(crc32.getValue());
zos.putNextEntry(e);
zos.write(toWrite);
}
} catch (IOException e) {
// some systems mightn't allow file to be updated while open
System.err.println("error updating file. " + e);
return false;
}
return true;
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4174691
@summary Test if the constructor will check for null input.
*/
import java.util.zip.ZipInputStream;
public class Constructor {
public static void main(String[] args) throws Exception {
try {
ZipInputStream in = new ZipInputStream(null);
throw new Exception("Constructor did not check the null argument");
} catch (NullPointerException e) {
}
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4029427
@summary Make sure ZipInputStream.skip(n) will check
for negative n.
*/
import java.io.*;
import java.util.zip.*;
public class Skip {
public static void main(String[] args) throws Exception {
ZipInputStream z = new ZipInputStream(System.in);
try {
z.skip(-1);
throw new Exception("Skip allowed negative value");
} catch (IllegalArgumentException e) {
}
}
}

View file

@ -0,0 +1,282 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8303866
* @summary ZipInputStream should read 8-byte data descriptors if the LOC has
* a ZIP64 extended information extra field
* @run junit Zip64DataDescriptor
*/
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.HexFormat;
import java.util.zip.*;
import static org.junit.jupiter.api.Assertions.*;
public class Zip64DataDescriptor {
// A byte array holding a small-sized Zip64 ZIP file, described below
private byte[] zip64File;
// A byte array holding a ZIP used for testing invalid Zip64 extra fields
private byte[] invalidZip64;
@BeforeEach
public void setup() throws IOException {
/*
* Structure of the ZIP64 file used below . Note the presence
* of a Zip64 extended information extra field and the
* Data Descriptor having 8-byte values for csize and size.
*
* The file was produced using the zip command on MacOS
* (zip 3.0, by Info-ZIP), in streamming mode (to enable Zip64),
* using the -fd option (to force the use of data descriptors)
*
* The following command was used:
* <pre>echo hello | zip -fd > hello.zip</pre>
*
* ------ Local File Header ------
* 000000 signature 0x04034b50
* 000004 version 45
* 000006 flags 0x0008
* 000008 method 8 Deflated
* 000010 time 0xb180 22:12
* 000012 date 0x565c 2023-02-28
* 000014 crc 0x00000000
* 000018 csize -1
* 000022 size -1
* 000026 nlen 1
* 000028 elen 20
* 000030 name 1 bytes '-'
* 000031 ext id 0x0001 Zip64 extended information extra field
* 000033 ext size 16
* 000035 z64 size 0
* 000043 z64 csize 0
*
* ------ File Data ------
* 000051 data 8 bytes
*
* ------ Data Desciptor ------
* 000059 signature 0x08074b50
* 000063 crc 0x363a3020
* 000067 csize 8
* 000075 size 6
* 000083 ...
*/
String hex = """
504b03042d000800080080b15c5600000000ffffffffffffffff01001400
2d0100100000000000000000000000000000000000cb48cdc9c9e7020050
4b070820303a3608000000000000000600000000000000504b01021e032d
000800080080b15c5620303a360800000006000000010000000000000001
000000b011000000002d504b050600000000010001002f00000053000000
0000""";
zip64File = HexFormat.of().parseHex(hex.replaceAll("\n", ""));
// Create the ZIP file used for testing that invalid Zip64 extra fields are ignored
// This ZIP has the regular 4-bit data descriptor
byte[] extra = new byte[Long.BYTES + Long.BYTES + Short.BYTES * 2]; // Size of a regular Zip64 extra field
ByteBuffer buffer = ByteBuffer.wrap(extra).order(ByteOrder.LITTLE_ENDIAN);
buffer.putShort(0, (short) 123); // Not processed by ZipEntry.setExtra
buffer.putShort(Short.BYTES, (short) (extra.length - 4));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zo = new ZipOutputStream(baos)) {
ZipEntry ze = new ZipEntry("-");
ze.setExtra(extra);
zo.putNextEntry(ze);
zo.write("hello\n".getBytes(StandardCharsets.UTF_8));
}
invalidZip64 = baos.toByteArray();
// Set Zip64 magic values on compressed and uncompressed size fields
ByteBuffer.wrap(invalidZip64).order(ByteOrder.LITTLE_ENDIAN)
.putInt(ZipFile.LOCSIZ, 0xFFFFFFFF)
.putInt(ZipFile.LOCLEN, 0xFFFFFFFF);
// Set the Zip64 header ID 0x1 on the extra field in the invalid file
setExtraHeaderId((short) 0x1);
}
/*
* Verify that small-sized Zip64 entries can be parsed by ZipInputStream
*/
@Test
public void shouldReadZip64Descriptor() throws IOException {
readZipInputStream(zip64File);
}
/*
* For maximal backward compatibility when reading Zip64 descriptors, invalid
* Zip64 extra data sizes should be ignored
*/
@Test
public void shouldIgnoreInvalidExtraSize() throws IOException {
setExtraSize((short) 42);
readZipInputStream(invalidZip64);
}
/*
* Files with Zip64 magic values but no Zip64 field should be ignored
* when considering 8 byte data descriptors
*/
@Test
public void shouldIgnoreNoZip64Header() throws IOException {
setExtraSize((short) 123);
readZipInputStream(invalidZip64);
}
/*
* Theoretically, ZIP files may exist with ZIP64 format, but with 4-byte
* data descriptors. Such files will fail to parse, as demonstrated by this test.
*/
@Test
public void shouldFailParsingZip64With4ByteDataDescriptor() throws IOException {
ZipException ex = assertThrows(ZipException.class, () -> {
readZipInputStream(invalidZip64);
});
String msg = String.format("Expected exeption message to contain 'invalid entry size', was %s",
ex.getMessage());
assertTrue(ex.getMessage().contains("invalid entry size"), msg);
}
/*
* Validate that an extra data size exceeding the length of the extra field is ignored
*/
@Test
public void shouldIgnoreExcessiveExtraSize() throws IOException {
setExtraSize(Short.MAX_VALUE);
readZipInputStream(invalidZip64);
}
/*
* Validate that the Data Descriptor is read with 32-bit fields if neither the
* LOC's 'uncompressed size' or 'compressed size' fields have the Zip64 magic value,
* even when there is a Zip64 field in the extra field.
*/
@Test
public void shouldIgnoreNoMagicMarkers() throws IOException {
// Set compressed and uncompressed size fields to zero
ByteBuffer.wrap(invalidZip64).order(ByteOrder.LITTLE_ENDIAN)
.putInt(ZipFile.LOCSIZ, 0)
.putInt(ZipFile.LOCLEN, 0);
readZipInputStream(invalidZip64);
}
/*
* Validate that an extra data size exceeding the length of the extra field is ignored
*/
@Test
public void shouldIgnoreTrucatedZip64Extra() throws IOException {
truncateZip64();
readZipInputStream(invalidZip64);
}
/**
* Update the Extra field header ID of the invalid file
*/
private void setExtraHeaderId(short id) {
// Set the header ID on the extra field
ByteBuffer buffer = ByteBuffer.wrap(invalidZip64).order(ByteOrder.LITTLE_ENDIAN);
int nlen = buffer.getShort(ZipFile.LOCNAM);
buffer.putShort(ZipFile.LOCHDR + nlen, id);
}
/**
* Updates the 16-bit 'data size' field of the Zip64 extended information field,
* potentially to an invalid value.
* @param size the value to set in the 'data size' field.
*/
private void setExtraSize(short size) {
ByteBuffer buffer = ByteBuffer.wrap(invalidZip64).order(ByteOrder.LITTLE_ENDIAN);
// Compute the offset to the Zip64 data block size field
short nlen = buffer.getShort(ZipFile.LOCNAM);
int dataSizeOffset = ZipFile.LOCHDR + nlen + Short.BYTES;
buffer.putShort(dataSizeOffset, size);
}
/**
* Puts a truncated Zip64 field (just the tag) at the end of the LOC extra field.
* The beginning of the extra field is filled with a generic extra field containing
* just zeros.
*/
private void truncateZip64() {
ByteBuffer buffer = ByteBuffer.wrap(invalidZip64).order(ByteOrder.LITTLE_ENDIAN);
// Get the LOC name and extra sizes
short nlen = buffer.getShort(ZipFile.LOCNAM);
short elen = buffer.getShort(ZipFile.LOCEXT);
int cenOffset = ZipFile.LOCHDR + nlen + elen;
// Zero out the extra field
int estart = ZipFile.LOCHDR + nlen;
buffer.put(estart, new byte[elen]);
// Put a generic extra field in the start
buffer.putShort(estart, (short) 42);
buffer.putShort(estart + Short.BYTES, (short) (elen - 4 - 2));
// Put a truncated (just the tag) Zip64 field at the end
buffer.putShort(cenOffset - Short.BYTES, (short) 0x0001);
}
/*
* Consume and verify the ZIP file using ZipInputStream
*/
private void readZipInputStream(byte[] zip) throws IOException {
try (ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(zip))) {
// Read the ZIP entry, this calls readLOC
ZipEntry e = in.getNextEntry();
// Sanity check the zip entry
assertNotNull(e, "Missing zip entry");
assertEquals("-", e.getName());
// Read the entry data, this causes readEND to parse the data descriptor
assertEquals("hello\n", new String(in.readAllBytes(), StandardCharsets.UTF_8));
// There should only be a single zip entry
assertNull(in.getNextEntry(), "Unexpected additional zip entry");
}
}
}

View file

@ -0,0 +1,261 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 8313739
@summary Verify that ZipOutputStream closes the wrapped stream even after failed writes
@run junit CloseWrappedStream
*/
import org.junit.jupiter.api.Test;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.*;
public class CloseWrappedStream {
/**
* Verify that closing a ZipOutputStream closes the wrapped output stream,
* also when the wrapped stream throws while remaining data is flushed
*/
@Test
public void exceptionDuringFinish() {
// A wrapped stream which should be closed even after a write failure
WrappedOutputStream wrappedStream = new WrappedOutputStream();
IOException exception = assertThrows(IOException.class, () -> {
try (ZipOutputStream zo = new ZipOutputStream(wrappedStream)) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
// Make finish() throw IOException
wrappedStream.failOnWrite = true;
} // Close throws when deflated data is flushed to wrapped stream
});
// Check that we failed with the expected message
assertEquals(WrappedOutputStream.WRITE_MSG, exception.getMessage());
// Verify that the wrapped stream was closed once
assertEquals(1, wrappedStream.timesClosed,
"Expected wrapped output stream to be closed once");
}
/**
* Sanity check that the wrapped stream is closed also for the
* normal case where the wrapped stream does not throw
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void noExceptions() throws IOException {
WrappedOutputStream wrappedStream = new WrappedOutputStream();
try (ZipOutputStream zo = new ZipOutputStream(wrappedStream)) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
}
// Verify that the wrapped stream was closed once
assertEquals(1, wrappedStream.timesClosed,
"Expected wrapped output stream to be closed once");
}
/**
* Check that an exception thrown while closing the wrapped
* stream is propagated to the caller without any suppressed exceptions
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void exceptionDuringClose() throws IOException {
WrappedOutputStream wrappedStream = new WrappedOutputStream();
IOException exception = assertThrows(IOException.class, () -> {
try (ZipOutputStream zo = new ZipOutputStream(wrappedStream)) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
wrappedStream.failOnClose = true;
}
});
// Check that we failed with the expected message
assertEquals(WrappedOutputStream.CLOSE_MSG, exception.getMessage());
// There should be no suppressed exceptions
assertEquals(0, exception.getSuppressed().length);
// Verify that the wrapped stream was closed once
assertEquals(1, wrappedStream.timesClosed,
"Expected wrapped output stream to be closed once");
}
/**
* Check that if an exception is thrown while closing the wrapped stream,
* then later close attempts will not close the wrapped stream again
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void doubleCloseShouldCloseWrappedStreamOnce() throws IOException {
WrappedOutputStream wrappedStream = new WrappedOutputStream();
// Keep a reference so we can double-close
AtomicReference<ZipOutputStream> ref = new AtomicReference<>();
IOException exception = assertThrows(IOException.class, () -> {
try (ZipOutputStream zo = new ZipOutputStream(wrappedStream)) {
ref.set(zo);
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
wrappedStream.failOnClose = true;
}
});
// Double-closing the ZipOutputStream should have no effect
ref.get().close();
// Check that we failed with the expected message
assertEquals(WrappedOutputStream.CLOSE_MSG, exception.getMessage());
// There should be no suppressed exceptions
assertEquals(0, exception.getSuppressed().length);
// Verify that the wrapped stream was closed once
assertEquals(1, wrappedStream.timesClosed,
"Expected wrapped output stream to be closed once");
}
/**
* Check that when the wrapped stream throws while calling finish
* AND while being closed, then the propagated exception is the one
* from the close operation, with the exception thrown during finish
* added as a suppressed exception.
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void exceptionDuringFinishAndClose() throws IOException {
WrappedOutputStream wrappedStream = new WrappedOutputStream();
IOException exception = assertThrows(IOException.class, () -> {
try (ZipOutputStream zo = new ZipOutputStream(wrappedStream)) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
// Will cause wrapped stream to throw during finish()
wrappedStream.failOnWrite = true;
// Will cause wrapped stream to throw during close()
wrappedStream.failOnClose = true;
}
});
// We expect the top-level exception to be from the close operation
assertEquals(WrappedOutputStream.CLOSE_MSG, exception.getMessage());
// The exception from the finish operation should be suppressed
assertEquals(1, exception.getSuppressed().length, "Expected suppressed exception");
assertEquals(WrappedOutputStream.WRITE_MSG, exception.getSuppressed()[0].getMessage());
// Verify that the wrapped stream was closed once
assertEquals(1, wrappedStream.timesClosed,
"Expected wrapped output stream to be closed once");
}
/**
* Check that when the wrapped stream throws the same IOException
* (identical instance) for write and close operations, then no attempt
* is made to add the exception instance to itself as a suppressed exception.
*
* @throws IOException if an unexpected IOException occurs
*/
@Test
public void sameExceptionDuringFinishAndClose() throws IOException {
WrappedOutputStream wrappedStream = new WrappedOutputStream();
IOException exception = assertThrows(IOException.class, () -> {
try (ZipOutputStream zo = new ZipOutputStream(wrappedStream)) {
zo.putNextEntry(new ZipEntry("file.txt"));
zo.write("hello".getBytes(StandardCharsets.UTF_8));
// Make operations fail with the same exception instance
wrappedStream.failException = new IOException("same fail");
wrappedStream.failOnWrite = true;
wrappedStream.failOnClose = true;
}
});
// We expect the top-level exception to be from the close operation
assertSame(wrappedStream.failException, exception);
// The exception should not suppress itself
assertEquals(0, exception.getSuppressed().length, "Expected no suppressed exception");
// Verify that the wrapped stream was closed once
assertEquals(1, wrappedStream.timesClosed,
"Expected wrapped output stream to be closed once");
}
/**
* Output stream which conditionally throws IOException on writes
* and tracks its close status.
*/
static class WrappedOutputStream extends FilterOutputStream {
static final String WRITE_MSG = "fail during write";
static final String CLOSE_MSG = "fail during close";
boolean failOnWrite = false;
boolean failOnClose = false;
IOException failException = null;
int timesClosed = 0;
public WrappedOutputStream() {
super(new ByteArrayOutputStream());
}
@Override
public synchronized void write(byte[] b, int off, int len) throws IOException{
if (failOnWrite) {
throw failException != null ? failException : new IOException(WRITE_MSG);
} else {
super.write(b, off, len);
}
}
@Override
public void close() throws IOException {
timesClosed++;
if (failOnClose) {
throw failException != null ? failException : new IOException(CLOSE_MSG);
} else {
super.close();
}
}
}
}

View file

@ -0,0 +1,114 @@
/*
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.ByteArrayOutputStream;
import java.util.function.Consumer;
import java.util.stream.IntStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @test
* @bug 8277087
* @summary Verifies various use cases when the zip comment should be empty
* @run junit EmptyComment
*/
public final class EmptyComment {
static IntStream longLengths() {
return IntStream.of(0xFFFF + 1, 0xFFFF + 2, 0xFFFF * 2);
}
/**
* Overflow, the text is too long to be stored as a comment.
*/
@ParameterizedTest
@MethodSource("longLengths")
void testOverflow(int length) throws Exception {
test(zos -> assertThrows(IllegalArgumentException.class, () -> {
zos.setComment("X".repeat(length));
}));
}
/**
* Simple cases where the comment is set to the empty text.
*/
@Test
void testSimpleCases() throws Exception {
test(zos -> {/* do nothing */});
test(zos -> zos.setComment(null));
test(zos -> zos.setComment(""));
test(zos -> {
zos.setComment("");
zos.setComment(null);
});
test(zos -> {
zos.setComment(null);
zos.setComment("");
});
test(zos -> {
zos.setComment("Comment");
zos.setComment(null);
});
test(zos -> {
zos.setComment("Comment");
zos.setComment("");
});
}
private static void test(Consumer<ZipOutputStream> test) throws Exception {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos)) {
test.accept(zos);
zos.putNextEntry(new ZipEntry("x"));
zos.finish();
byte[] data = baos.toByteArray();
if (data.length > 0xFFFF) { // just in case
throw new RuntimeException("data is too big: " + data.length);
}
int pk = data.length - ZipFile.ENDHDR;
if (data[pk] != 'P' || data[pk + 1] != 'K') {
throw new RuntimeException("PK is not found");
}
// Since the comment is empty this will be two last bytes
int pos = data.length - ZipFile.ENDHDR + ZipFile.ENDCOM;
int len = (data[pos] & 0xFF) + ((data[pos + 1] & 0xFF) << 8);
if (len != 0) {
throw new RuntimeException("zip comment is not empty: " + len);
}
}
}
}

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