From 9620f9889e6fd121a39f4e2ea9811dcfca81d95e Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 14 May 2025 01:53:19 +0000 Subject: [PATCH 1/2] 8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset 8355975: ZipFile uses incorrect Charset if another instance for the same ZIP file was constructed with a different Charset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eirik Bjørsnøs Reviewed-by: eirbjo, lancea, redestad, alanb --- .../share/classes/java/util/zip/ZipCoder.java | 22 ++ .../share/classes/java/util/zip/ZipFile.java | 281 +++++++++++++++--- .../util/zip/ZipFile/ZipFileCharsetTest.java | 125 ++++++++ .../zip/ZipFile/ZipFileSharedSourceTest.java | 137 +++++++++ 4 files changed, 529 insertions(+), 36 deletions(-) create mode 100644 test/jdk/java/util/zip/ZipFile/ZipFileCharsetTest.java create mode 100644 test/jdk/java/util/zip/ZipFile/ZipFileSharedSourceTest.java diff --git a/src/java.base/share/classes/java/util/zip/ZipCoder.java b/src/java.base/share/classes/java/util/zip/ZipCoder.java index 8696d2a797e..bae3ed9e3bf 100644 --- a/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -1,5 +1,11 @@ /* +<<<<<<< HEAD * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. +======= + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +44,16 @@ import sun.nio.cs.UTF_8; /** +<<<<<<< HEAD * Utility class for zipfile name and comment decoding and encoding +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + * Utility class for ZIP file entry name and comment decoding and encoding +======= + * Utility class for ZIP file entry name and comment decoding and encoding. + *

+ * The {@code ZipCoder} for UTF-8 charset is thread safe, {@code ZipCoder} + * for other charsets require external synchronization. +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) */ class ZipCoder { @@ -182,6 +197,13 @@ protected CharsetDecoder decoder() { return dec; } + /** + * {@return the {@link Charset} used by this {@code ZipCoder}} + */ + final Charset charset() { + return this.cs; + } + private CharsetEncoder encoder() { if (enc == null) { enc = cs.newEncoder() diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index cb9070fc885..c6487b9cf63 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1,5 +1,11 @@ /* +<<<<<<< HEAD * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. +======= + * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,7 +101,17 @@ */ public class ZipFile implements ZipConstants, Closeable { +<<<<<<< HEAD private final String name; // zip file name +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + private final String filePath; // ZIP file path + private final String fileName; // name of the file +======= + private final String filePath; // ZIP file path + private final String fileName; // name of the file + // Used when decoding entry names and comments + private final ZipCoder zipCoder; +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) private volatile boolean closeRequested; // The "resource" used by this zip file that needs to be @@ -248,7 +264,8 @@ public ZipFile(File file, int mode, Charset charset) throws IOException this.name = name; long t0 = System.nanoTime(); - this.res = new CleanableResource(this, ZipCoder.get(charset), file, mode); + this.zipCoder = ZipCoder.get(charset); + this.res = new CleanableResource(this, zipCoder, file, mode); PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0); PerfCounter.getZipFileCount().increment(); @@ -319,7 +336,25 @@ public String getComment() { if (res.zsrc.comment == null) { return null; } +<<<<<<< HEAD return res.zsrc.zc.toString(res.zsrc.comment); +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + // If there is a problem decoding the byte array which represents + // the ZIP file comment, return null; + try { + return res.zsrc.zc.toString(res.zsrc.comment); + } catch (IllegalArgumentException iae) { + return null; + } +======= + // If there is a problem decoding the byte array which represents + // the ZIP file comment, return null; + try { + return zipCoder.toString(res.zsrc.comment); + } catch (IllegalArgumentException iae) { + return null; + } +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) } } @@ -336,9 +371,25 @@ public ZipEntry getEntry(String name) { ZipEntry entry = null; synchronized (this) { ensureOpen(); +<<<<<<< HEAD int pos = res.zsrc.getEntryPos(name, true); if (pos != -1) { entry = getZipEntry(name, pos); +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + // Look up the name and CEN header position of the entry. + // The resolved name may include a trailing slash. + // See Source::getEntryPos for details. + EntryPos pos = res.zsrc.getEntryPos(name, true); + if (pos != null) { + entry = getZipEntry(pos.name, pos.pos); +======= + // Look up the name and CEN header position of the entry. + // The resolved name may include a trailing slash. + // See Source::getEntryPos for details. + EntryPos pos = res.zsrc.getEntryPos(name, true, zipCoder); + if (pos != null) { + entry = getZipEntry(pos.name, pos.pos); +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) } } return entry; @@ -376,7 +427,23 @@ public InputStream getInputStream(ZipEntry entry) throws IOException { if (Objects.equals(lastEntryName, entry.name)) { pos = lastEntryPos; } else { +<<<<<<< HEAD pos = zsrc.getEntryPos(entry.name, false); +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + EntryPos entryPos = zsrc.getEntryPos(entry.name, false); + if (entryPos != null) { + pos = entryPos.pos; + } else { + pos = -1; + } +======= + EntryPos entryPos = zsrc.getEntryPos(entry.name, false, zipCoder); + if (entryPos != null) { + pos = entryPos.pos; + } else { + pos = -1; + } +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) } if (pos == -1) { return null; @@ -409,6 +476,35 @@ public InputStream getInputStream(ZipEntry entry) throws IOException { } } + /** + * Determines and returns a {@link ZipCoder} to use for decoding + * name and comment fields of the ZIP entry identified by the {@code pos} + * in the ZIP file's {@code cen}. + *

+ * A ZIP entry's name and comment fields may be encoded using UTF-8, in + * which case this method returns a UTF-8 capable {@code ZipCoder}. If the + * entry doesn't require UTF-8, then this method returns the {@code fallback} + * {@code ZipCoder}. + * + * @param cen the CEN + * @param pos the ZIP entry's position in CEN + * @param fallback the fallback ZipCoder to return if the entry doesn't require UTF-8 + */ + private static ZipCoder zipCoderFor(final byte[] cen, final int pos, final ZipCoder fallback) { + if (fallback.isUTF8()) { + // the fallback ZipCoder is capable of handling UTF-8, + // so no need to parse the entry flags to determine if + // the entry has UTF-8 flag. + return fallback; + } + if ((CENFLG(cen, pos) & USE_UTF8) != 0) { + // entry requires a UTF-8 ZipCoder + return ZipCoder.UTF8; + } + // entry doesn't require a UTF-8 ZipCoder + return fallback; + } + private static class InflaterCleanupAction implements Runnable { private final Inflater inf; private final CleanableResource res; @@ -599,7 +695,7 @@ public Stream stream() { private String getEntryName(int pos) { byte[] cen = res.zsrc.cen; int nlen = CENNAM(cen, pos); - ZipCoder zc = res.zsrc.zipCoderForPos(pos); + ZipCoder zc = zipCoderFor(cen, pos, zipCoder); return zc.toString(cen, pos + CENHDR, nlen); } @@ -686,6 +782,12 @@ private ZipEntry getZipEntry(String name, int pos) { } if (clen != 0) { int start = pos + CENHDR + nlen + elen; +<<<<<<< HEAD +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + ZipCoder zc = res.zsrc.zipCoderForPos(pos); +======= + ZipCoder zc = zipCoderFor(cen, pos, zipCoder); +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) e.comment = zc.toString(cen, start, clen); } lastEntryName = e.name; @@ -717,11 +819,12 @@ private static class CleanableResource implements Runnable { Source zsrc; - CleanableResource(ZipFile zf, ZipCoder zc, File file, int mode) throws IOException { + CleanableResource(ZipFile zf, ZipCoder zipCoder, File file, int mode) throws IOException { + assert zipCoder != null : "null ZipCoder"; this.cleanable = CleanerFactory.cleaner().register(zf, this); this.istreams = Collections.newSetFromMap(new WeakHashMap<>()); this.inflaterCache = new ArrayDeque<>(); - this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0, zc); + this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0, zipCoder); } void clean() { @@ -1157,6 +1260,7 @@ public void setExtraAttributes(ZipEntry ze, int extraAttrs) { ); } + // Implementation note: This class is thread safe. private static class Source { // While this is only used from ZipFile, defining it there would cause // a bootstrap cycle that would leave this initialized as null @@ -1166,7 +1270,12 @@ private static class Source { private static final int[] EMPTY_META_VERSIONS = new int[0]; private final Key key; // the key in files +<<<<<<< HEAD private final @Stable ZipCoder zc; // zip coder used to decode/encode +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + private final @Stable ZipCoder zc; // ZIP coder used to decode/encode +======= +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) private int refs = 1; @@ -1202,8 +1311,9 @@ private static class Source { private int[] entries; // array of hashed cen entry // Checks the entry at offset pos in the CEN, calculates the Entry values as per above, - // then returns the length of the entry name. - private int checkAndAddEntry(int pos, int index) + // then returns the length of the entry name. Uses the given zipCoder for processing the + // entry name and the entry comment (if any). + private int checkAndAddEntry(final int pos, final int index, final ZipCoder zipCoder) throws ZipException { byte[] cen = this.cen; @@ -1234,22 +1344,35 @@ private int checkAndAddEntry(int pos, int index) } try { - ZipCoder zcp = zipCoderForPos(pos); - int hash = zcp.checkedHash(cen, entryPos, nlen); + int hash = zipCoder.checkedHash(cen, entryPos, nlen); int hsh = (hash & 0x7fffffff) % tablelen; int next = table[hsh]; table[hsh] = index; // Record the CEN offset and the name hash in our hash cell. +<<<<<<< HEAD entries[index++] = hash; entries[index++] = next; entries[index ] = pos; // Validate comment if it exists // if the bytes representing the comment cannot be converted to +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + entries[index++] = hash; + entries[index++] = next; + entries[index ] = pos; + // Validate comment if it exists. + // If the bytes representing the comment cannot be converted to +======= + entries[index] = hash; + entries[index + 1] = next; + entries[index + 2] = pos; + // Validate comment if it exists. + // If the bytes representing the comment cannot be converted to +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) // a String via zcp.toString, an Exception will be thrown int clen = CENCOM(cen, pos); if (clen > 0) { int start = entryPos + nlen + elen; - zcp.toString(cen, start, clen); + zipCoder.toString(cen, start, clen); } } catch (Exception e) { zerror("invalid CEN header (bad entry name or comment)"); @@ -1395,26 +1518,57 @@ private static boolean isZip64ExtBlockSizeValid(int blockSize) { private int[] table; // Hash chain heads: indexes into entries private int tablelen; // number of hash heads +<<<<<<< HEAD +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + /** + * A class representing a key to a ZIP file. A key is based + * on the file key if available, or the path value if the + * file key is not available. The key is also based on the + * file's last modified time to allow for cases where a ZIP + * file is re-opened after it has been modified. + */ +======= + /** + * A class representing a key to the Source of a ZipFile. + * The Key is composed of: + * - The BasicFileAttributes.fileKey() if available, or the Path of the ZIP file + * if the fileKey() is not available. + * - The ZIP file's last modified time (to allow for cases + * where a ZIP file is re-opened after it has been modified). + * - The Charset that was provided when constructing the ZipFile instance. + * The unique combination of these components identifies a Source of a ZipFile. + */ +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) private static class Key { - final BasicFileAttributes attrs; - File file; - final boolean utf8; - - public Key(File file, BasicFileAttributes attrs, ZipCoder zc) { + private final BasicFileAttributes attrs; + private final File file; + // the Charset that was provided when constructing the ZipFile instance + private final Charset charset; + + /** + * Constructs a {@code Key} to a {@code Source} of a {@code ZipFile} + * + * @param file the ZIP file + * @param attrs the attributes of the ZIP file + * @param charset the Charset that was provided when constructing the ZipFile instance + */ + public Key(File file, BasicFileAttributes attrs, Charset charset) { this.attrs = attrs; this.file = file; - this.utf8 = zc.isUTF8(); + this.charset = charset; } + @Override public int hashCode() { - long t = utf8 ? 0 : Long.MAX_VALUE; + long t = charset.hashCode(); t += attrs.lastModifiedTime().toMillis(); return ((int)(t ^ (t >>> 32))) + file.hashCode(); } + @Override public boolean equals(Object obj) { if (obj instanceof Key key) { - if (key.utf8 != utf8) { + if (!charset.equals(key.charset)) { return false; } if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { @@ -1438,12 +1592,12 @@ public boolean equals(Object obj) { private static final java.nio.file.FileSystem builtInFS = DefaultFileSystemProvider.theFileSystem(); - static Source get(File file, boolean toDelete, ZipCoder zc) throws IOException { + static Source get(File file, boolean toDelete, ZipCoder zipCoder) throws IOException { final Key key; try { key = new Key(file, Files.readAttributes(builtInFS.getPath(file.getPath()), - BasicFileAttributes.class), zc); + BasicFileAttributes.class), zipCoder.charset()); } catch (InvalidPathException ipe) { throw new IOException(ipe); } @@ -1455,7 +1609,7 @@ static Source get(File file, boolean toDelete, ZipCoder zc) throws IOException { return src; } } - src = new Source(key, toDelete, zc); + src = new Source(key, toDelete, zipCoder); synchronized (files) { Source prev = files.putIfAbsent(key, src); @@ -1477,8 +1631,7 @@ static void release(Source src) throws IOException { } } - private Source(Key key, boolean toDelete, ZipCoder zc) throws IOException { - this.zc = zc; + private Source(Key key, boolean toDelete, ZipCoder zipCoder) throws IOException { this.key = key; if (toDelete) { if (OperatingSystem.isWindows()) { @@ -1492,7 +1645,7 @@ private Source(Key key, boolean toDelete, ZipCoder zc) throws IOException { this.zfile = new RandomAccessFile(key.file, "r"); } try { - initCEN(-1); + initCEN(-1, zipCoder); byte[] buf = new byte[4]; readFullyAt(buf, 0, 4, 0); this.startsWithLoc = (LOCSIG(buf) == LOCSIG); @@ -1649,8 +1802,16 @@ private End findEND() throws IOException { throw new ZipException("zip END header not found"); } +<<<<<<< HEAD // Reads zip file central directory. private void initCEN(int knownTotal) throws IOException { +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + // Reads ZIP file central directory. + private void initCEN(int knownTotal) throws IOException { +======= + // Reads ZIP file central directory. + private void initCEN(final int knownTotal, final ZipCoder zipCoder) throws IOException { +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) // Prefer locals for better performance during startup byte[] cen; if (knownTotal == -1) { @@ -1712,12 +1873,26 @@ private void initCEN(int knownTotal) throws IOException { // This will only happen if the zip file has an incorrect // ENDTOT field, which usually means it contains more than // 65535 entries. +<<<<<<< HEAD initCEN(countCENHeaders(cen, limit)); +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + initCEN(countCENHeaders(cen)); +======= + initCEN(countCENHeaders(cen), zipCoder); +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) return; } +<<<<<<< HEAD +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + int entryPos = pos + CENHDR; +======= + int entryPos = pos + CENHDR; + // the ZipCoder for any non-UTF8 entries + final ZipCoder entryZipCoder = zipCoderFor(cen, pos, zipCoder); +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) // Checks the entry and adds values to entries[idx ... idx+2] - int nlen = checkAndAddEntry(pos, idx); + int nlen = checkAndAddEntry(pos, idx, entryZipCoder); idx += 3; // Adds name to metanames. @@ -1738,9 +1913,39 @@ private void initCEN(int knownTotal) throws IOException { // performance in multi-release jar files int version = getMetaVersion(entryPos + META_INF_LEN, nlen - META_INF_LEN); if (version > 0) { +<<<<<<< HEAD if (metaVersionsSet == null) metaVersionsSet = new TreeSet<>(); metaVersionsSet.add(version); +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + try { + // Compute hash code of name from "META-INF/versions/{version)/{name} + int prefixLen = META_INF_VERSIONS_LEN + DecimalDigits.stringSize(version); + int hashCode = zipCoderForPos(pos).checkedHash(cen, + entryPos + prefixLen, + nlen - prefixLen); + // Register version for this hash code + if (metaVersions == null) + metaVersions = new HashMap<>(); + metaVersions.computeIfAbsent(hashCode, _ -> new BitSet()).set(version); + } catch (Exception e) { + zerror("invalid CEN header (bad entry name or comment)"); + } +======= + try { + // Compute hash code of name from "META-INF/versions/{version)/{name} + int prefixLen = META_INF_VERSIONS_LEN + DecimalDigits.stringSize(version); + int hashCode = entryZipCoder.checkedHash(cen, + entryPos + prefixLen, + nlen - prefixLen); + // Register version for this hash code + if (metaVersions == null) + metaVersions = new HashMap<>(); + metaVersions.computeIfAbsent(hashCode, _ -> new BitSet()).set(version); + } catch (Exception e) { + zerror("invalid CEN header (bad entry name or comment)"); + } +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) } } } @@ -1782,10 +1987,25 @@ private static void zerror(String msg) throws ZipException { } /* +<<<<<<< HEAD * Returns the {@code pos} of the zip cen entry corresponding to the * specified entry name, or -1 if not found. +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + * Returns the resolved name and position of the ZIP cen entry corresponding + * to the specified entry name, or {@code null} if not found. +======= + * Returns the resolved name and position of the ZIP cen entry corresponding + * to the specified entry name, or {@code null} if not found. +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) */ +<<<<<<< HEAD private int getEntryPos(String name, boolean addSlash) { +||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + private EntryPos getEntryPos(String name, boolean addSlash) { +======= + private EntryPos getEntryPos(final String name, final boolean addSlash, + final ZipCoder zipCoder) { +>>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) if (total == 0) { return -1; } @@ -1804,8 +2024,7 @@ private int getEntryPos(String name, boolean addSlash) { int noff = pos + CENHDR; int nlen = CENNAM(cen, pos); - ZipCoder zc = zipCoderForPos(pos); - + final ZipCoder zc = zipCoderFor(cen, pos, zipCoder); // Compare the lookup name with the name encoded in the CEN switch (zc.compare(name, cen, noff, nlen, addSlash)) { case EXACT_MATCH: @@ -1831,16 +2050,6 @@ private int getEntryPos(String name, boolean addSlash) { return -1; } - private ZipCoder zipCoderForPos(int pos) { - if (zc.isUTF8()) { - return zc; - } - if ((CENFLG(cen, pos) & USE_UTF8) != 0) { - return ZipCoder.UTF8; - } - return zc; - } - /** * Returns true if the bytes represent a non-directory name * beginning with "META-INF/", disregarding ASCII case. diff --git a/test/jdk/java/util/zip/ZipFile/ZipFileCharsetTest.java b/test/jdk/java/util/zip/ZipFile/ZipFileCharsetTest.java new file mode 100644 index 00000000000..e97088247f4 --- /dev/null +++ b/test/jdk/java/util/zip/ZipFile/ZipFileCharsetTest.java @@ -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; + } +} diff --git a/test/jdk/java/util/zip/ZipFile/ZipFileSharedSourceTest.java b/test/jdk/java/util/zip/ZipFile/ZipFileSharedSourceTest.java new file mode 100644 index 00000000000..d4d057ede7b --- /dev/null +++ b/test/jdk/java/util/zip/ZipFile/ZipFileSharedSourceTest.java @@ -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 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> 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 f : results) { + f.get(); + } + } + System.out.println("All " + numTasks + " tasks completed successfully"); + } + + private static final class ZipEntryIteratingTask implements Callable { + 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; + } + } +} From 1752517b0c7544c72c90ca502f665095fc92d118 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Tue, 8 Jul 2025 14:57:33 +0200 Subject: [PATCH 2/2] resolve conflicts --- .../share/classes/java/util/zip/ZipCoder.java | 12 -- .../share/classes/java/util/zip/ZipFile.java | 180 +----------------- 2 files changed, 9 insertions(+), 183 deletions(-) diff --git a/src/java.base/share/classes/java/util/zip/ZipCoder.java b/src/java.base/share/classes/java/util/zip/ZipCoder.java index bae3ed9e3bf..afdfbcdf932 100644 --- a/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -1,11 +1,5 @@ /* -<<<<<<< HEAD - * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. -======= * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,16 +38,10 @@ import sun.nio.cs.UTF_8; /** -<<<<<<< HEAD - * Utility class for zipfile name and comment decoding and encoding -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - * Utility class for ZIP file entry name and comment decoding and encoding -======= * Utility class for ZIP file entry name and comment decoding and encoding. *

* The {@code ZipCoder} for UTF-8 charset is thread safe, {@code ZipCoder} * for other charsets require external synchronization. ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) */ class ZipCoder { diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index c6487b9cf63..07a7ef17b2e 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1,11 +1,5 @@ /* -<<<<<<< HEAD - * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. -======= * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,17 +95,9 @@ */ public class ZipFile implements ZipConstants, Closeable { -<<<<<<< HEAD private final String name; // zip file name -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - private final String filePath; // ZIP file path - private final String fileName; // name of the file -======= - private final String filePath; // ZIP file path - private final String fileName; // name of the file // Used when decoding entry names and comments private final ZipCoder zipCoder; ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) private volatile boolean closeRequested; // The "resource" used by this zip file that needs to be @@ -336,25 +322,7 @@ public String getComment() { if (res.zsrc.comment == null) { return null; } -<<<<<<< HEAD - return res.zsrc.zc.toString(res.zsrc.comment); -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - // If there is a problem decoding the byte array which represents - // the ZIP file comment, return null; - try { - return res.zsrc.zc.toString(res.zsrc.comment); - } catch (IllegalArgumentException iae) { - return null; - } -======= - // If there is a problem decoding the byte array which represents - // the ZIP file comment, return null; - try { - return zipCoder.toString(res.zsrc.comment); - } catch (IllegalArgumentException iae) { - return null; - } ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + return zipCoder.toString(res.zsrc.comment); } } @@ -371,25 +339,12 @@ public ZipEntry getEntry(String name) { ZipEntry entry = null; synchronized (this) { ensureOpen(); -<<<<<<< HEAD - int pos = res.zsrc.getEntryPos(name, true); - if (pos != -1) { - entry = getZipEntry(name, pos); -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - // Look up the name and CEN header position of the entry. - // The resolved name may include a trailing slash. - // See Source::getEntryPos for details. - EntryPos pos = res.zsrc.getEntryPos(name, true); - if (pos != null) { - entry = getZipEntry(pos.name, pos.pos); -======= // Look up the name and CEN header position of the entry. // The resolved name may include a trailing slash. // See Source::getEntryPos for details. - EntryPos pos = res.zsrc.getEntryPos(name, true, zipCoder); - if (pos != null) { - entry = getZipEntry(pos.name, pos.pos); ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + int pos = res.zsrc.getEntryPos(name, true, zipCoder); + if (pos != -1) { + entry = getZipEntry(name, pos); } } return entry; @@ -427,23 +382,7 @@ public InputStream getInputStream(ZipEntry entry) throws IOException { if (Objects.equals(lastEntryName, entry.name)) { pos = lastEntryPos; } else { -<<<<<<< HEAD - pos = zsrc.getEntryPos(entry.name, false); -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - EntryPos entryPos = zsrc.getEntryPos(entry.name, false); - if (entryPos != null) { - pos = entryPos.pos; - } else { - pos = -1; - } -======= - EntryPos entryPos = zsrc.getEntryPos(entry.name, false, zipCoder); - if (entryPos != null) { - pos = entryPos.pos; - } else { - pos = -1; - } ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + pos = zsrc.getEntryPos(entry.name, false, zipCoder); } if (pos == -1) { return null; @@ -745,7 +684,7 @@ private ZipEntry getZipEntry(String name, int pos) { int elen = CENEXT(cen, pos); int clen = CENCOM(cen, pos); - ZipCoder zc = res.zsrc.zipCoderForPos(pos); + ZipCoder zc = zipCoderFor(cen, pos, zipCoder); if (name != null) { // only need to check for mismatch of trailing slash if (nlen > 0 && @@ -782,12 +721,6 @@ private ZipEntry getZipEntry(String name, int pos) { } if (clen != 0) { int start = pos + CENHDR + nlen + elen; -<<<<<<< HEAD -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - ZipCoder zc = res.zsrc.zipCoderForPos(pos); -======= - ZipCoder zc = zipCoderFor(cen, pos, zipCoder); ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) e.comment = zc.toString(cen, start, clen); } lastEntryName = e.name; @@ -1270,12 +1203,6 @@ private static class Source { private static final int[] EMPTY_META_VERSIONS = new int[0]; private final Key key; // the key in files -<<<<<<< HEAD - private final @Stable ZipCoder zc; // zip coder used to decode/encode -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - private final @Stable ZipCoder zc; // ZIP coder used to decode/encode -======= ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) private int refs = 1; @@ -1349,25 +1276,11 @@ private int checkAndAddEntry(final int pos, final int index, final ZipCoder zipC int next = table[hsh]; table[hsh] = index; // Record the CEN offset and the name hash in our hash cell. -<<<<<<< HEAD - entries[index++] = hash; - entries[index++] = next; - entries[index ] = pos; - // Validate comment if it exists - // if the bytes representing the comment cannot be converted to -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - entries[index++] = hash; - entries[index++] = next; - entries[index ] = pos; - // Validate comment if it exists. - // If the bytes representing the comment cannot be converted to -======= entries[index] = hash; entries[index + 1] = next; entries[index + 2] = pos; // Validate comment if it exists. // If the bytes representing the comment cannot be converted to ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) // a String via zcp.toString, an Exception will be thrown int clen = CENCOM(cen, pos); if (clen > 0) { @@ -1518,16 +1431,6 @@ private static boolean isZip64ExtBlockSizeValid(int blockSize) { private int[] table; // Hash chain heads: indexes into entries private int tablelen; // number of hash heads -<<<<<<< HEAD -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - /** - * A class representing a key to a ZIP file. A key is based - * on the file key if available, or the path value if the - * file key is not available. The key is also based on the - * file's last modified time to allow for cases where a ZIP - * file is re-opened after it has been modified. - */ -======= /** * A class representing a key to the Source of a ZipFile. * The Key is composed of: @@ -1538,7 +1441,6 @@ private static boolean isZip64ExtBlockSizeValid(int blockSize) { * - The Charset that was provided when constructing the ZipFile instance. * The unique combination of these components identifies a Source of a ZipFile. */ ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) private static class Key { private final BasicFileAttributes attrs; private final File file; @@ -1802,16 +1704,8 @@ private End findEND() throws IOException { throw new ZipException("zip END header not found"); } -<<<<<<< HEAD - // Reads zip file central directory. - private void initCEN(int knownTotal) throws IOException { -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - // Reads ZIP file central directory. - private void initCEN(int knownTotal) throws IOException { -======= // Reads ZIP file central directory. private void initCEN(final int knownTotal, final ZipCoder zipCoder) throws IOException { ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) // Prefer locals for better performance during startup byte[] cen; if (knownTotal == -1) { @@ -1873,24 +1767,12 @@ private void initCEN(final int knownTotal, final ZipCoder zipCoder) throws IOExc // This will only happen if the zip file has an incorrect // ENDTOT field, which usually means it contains more than // 65535 entries. -<<<<<<< HEAD - initCEN(countCENHeaders(cen, limit)); -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - initCEN(countCENHeaders(cen)); -======= - initCEN(countCENHeaders(cen), zipCoder); ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + initCEN(countCENHeaders(cen, limit), zipCoder); return; } -<<<<<<< HEAD -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - int entryPos = pos + CENHDR; -======= - int entryPos = pos + CENHDR; // the ZipCoder for any non-UTF8 entries final ZipCoder entryZipCoder = zipCoderFor(cen, pos, zipCoder); ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) // Checks the entry and adds values to entries[idx ... idx+2] int nlen = checkAndAddEntry(pos, idx, entryZipCoder); idx += 3; @@ -1913,39 +1795,9 @@ private void initCEN(final int knownTotal, final ZipCoder zipCoder) throws IOExc // performance in multi-release jar files int version = getMetaVersion(entryPos + META_INF_LEN, nlen - META_INF_LEN); if (version > 0) { -<<<<<<< HEAD if (metaVersionsSet == null) metaVersionsSet = new TreeSet<>(); metaVersionsSet.add(version); -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - try { - // Compute hash code of name from "META-INF/versions/{version)/{name} - int prefixLen = META_INF_VERSIONS_LEN + DecimalDigits.stringSize(version); - int hashCode = zipCoderForPos(pos).checkedHash(cen, - entryPos + prefixLen, - nlen - prefixLen); - // Register version for this hash code - if (metaVersions == null) - metaVersions = new HashMap<>(); - metaVersions.computeIfAbsent(hashCode, _ -> new BitSet()).set(version); - } catch (Exception e) { - zerror("invalid CEN header (bad entry name or comment)"); - } -======= - try { - // Compute hash code of name from "META-INF/versions/{version)/{name} - int prefixLen = META_INF_VERSIONS_LEN + DecimalDigits.stringSize(version); - int hashCode = entryZipCoder.checkedHash(cen, - entryPos + prefixLen, - nlen - prefixLen); - // Register version for this hash code - if (metaVersions == null) - metaVersions = new HashMap<>(); - metaVersions.computeIfAbsent(hashCode, _ -> new BitSet()).set(version); - } catch (Exception e) { - zerror("invalid CEN header (bad entry name or comment)"); - } ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) } } } @@ -1987,25 +1839,11 @@ private static void zerror(String msg) throws ZipException { } /* -<<<<<<< HEAD - * Returns the {@code pos} of the zip cen entry corresponding to the - * specified entry name, or -1 if not found. -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - * Returns the resolved name and position of the ZIP cen entry corresponding - * to the specified entry name, or {@code null} if not found. -======= * Returns the resolved name and position of the ZIP cen entry corresponding * to the specified entry name, or {@code null} if not found. ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) */ -<<<<<<< HEAD - private int getEntryPos(String name, boolean addSlash) { -||||||| parent of 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) - private EntryPos getEntryPos(String name, boolean addSlash) { -======= - private EntryPos getEntryPos(final String name, final boolean addSlash, - final ZipCoder zipCoder) { ->>>>>>> 2c4e8d211a0 (8347712: IllegalStateException on multithreaded ZipFile access with non-UTF8 charset) + private int getEntryPos(final String name, final boolean addSlash, + final ZipCoder zipCoder) { if (total == 0) { return -1; }