8339874: Avoid duplicate checking of trailing slash in ZipFile.getZipEntry

Reviewed-by: lancea, redestad
This commit is contained in:
Eirik Bjørsnøs 2024-09-12 15:24:22 +00:00
parent 4d65c3efca
commit 7f1dae12e5
2 changed files with 34 additions and 63 deletions

View File

@ -158,13 +158,6 @@ class ZipCoder {
return hsh;
}
boolean hasTrailingSlash(byte[] a, int end) {
byte[] slashBytes = slashBytes();
return end >= slashBytes.length &&
Arrays.mismatch(a, end - slashBytes.length, end, slashBytes, 0, slashBytes.length) == -1;
}
private byte[] slashBytes;
private final Charset cs;
protected CharsetDecoder dec;
private CharsetEncoder enc;
@ -191,23 +184,6 @@ class ZipCoder {
return enc;
}
// This method produces an array with the bytes that will correspond to a
// trailing '/' in the chosen character encoding.
//
// While in most charsets a trailing slash will be encoded as the byte
// value of '/', this does not hold in the general case. E.g., in charsets
// such as UTF-16 and UTF-32 it will be represented by a sequence of 2 or 4
// bytes, respectively.
private byte[] slashBytes() {
if (slashBytes == null) {
// Take into account charsets that produce a BOM, e.g., UTF-16
byte[] slash = "/".getBytes(cs);
byte[] doubleSlash = "//".getBytes(cs);
slashBytes = Arrays.copyOfRange(doubleSlash, slash.length, doubleSlash.length);
}
return slashBytes;
}
/**
* This method is used by ZipFile.Source.getEntryPos when comparing the
* name being looked up to candidate names encoded in the CEN byte
@ -297,8 +273,7 @@ class ZipCoder {
return h;
}
@Override
boolean hasTrailingSlash(byte[] a, int end) {
private boolean hasTrailingSlash(byte[] a, int end) {
return end > 0 && a[end - 1] == '/';
}

View File

@ -347,9 +347,12 @@ public class ZipFile implements ZipConstants, Closeable {
ZipEntry entry = null;
synchronized (this) {
ensureOpen();
int pos = res.zsrc.getEntryPos(name, true);
if (pos != -1) {
entry = getZipEntry(name, 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);
if (pos != null) {
entry = getZipEntry(pos.name, pos.pos);
}
}
return entry;
@ -387,7 +390,12 @@ public class ZipFile implements ZipConstants, Closeable {
if (Objects.equals(lastEntryName, entry.name)) {
pos = lastEntryPos;
} else {
pos = zsrc.getEntryPos(entry.name, false);
EntryPos entryPos = zsrc.getEntryPos(entry.name, false);
if (entryPos != null) {
pos = entryPos.pos;
} else {
pos = -1;
}
}
if (pos == -1) {
return null;
@ -540,7 +548,8 @@ public class ZipFile implements ZipConstants, Closeable {
throw new NoSuchElementException();
}
// each "entry" has 3 ints in table entries
return (T)getZipEntry(null, res.zsrc.getEntryPos(i++ * 3));
int pos = res.zsrc.getEntryPos(i++ * 3);
return (T)getZipEntry(getEntryName(pos), pos);
}
}
@ -612,7 +621,7 @@ public class ZipFile implements ZipConstants, Closeable {
synchronized (this) {
ensureOpen();
return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
pos -> getZipEntry(null, pos)), false);
pos -> getZipEntry(getEntryName(pos), pos)), false);
}
}
@ -655,7 +664,7 @@ public class ZipFile implements ZipConstants, Closeable {
synchronized (this) {
ensureOpen();
return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
pos -> (JarEntry)getZipEntry(null, pos)), false);
pos -> (JarEntry)getZipEntry(getEntryName(pos), pos)), false);
}
}
@ -665,30 +674,10 @@ public class ZipFile implements ZipConstants, Closeable {
/* Check ensureOpen() before invoking this method */
private ZipEntry getZipEntry(String name, int pos) {
byte[] cen = res.zsrc.cen;
int nlen = CENNAM(cen, pos);
int elen = CENEXT(cen, pos);
int clen = CENCOM(cen, pos);
ZipEntry e = this instanceof JarFile jarFile
? Source.JUJA.entryFor(jarFile, name)
: new ZipEntry(name);
ZipCoder zc = res.zsrc.zipCoderForPos(pos);
if (name != null) {
// only need to check for mismatch of trailing slash
if (nlen > 0 &&
!name.isEmpty() &&
zc.hasTrailingSlash(cen, pos + CENHDR + nlen) &&
!name.endsWith("/"))
{
name += '/';
}
} else {
// invoked from iterator, use the entry name stored in cen
name = zc.toString(cen, pos + CENHDR, nlen);
}
ZipEntry e;
if (this instanceof JarFile) {
e = Source.JUJA.entryFor((JarFile)this, name);
} else {
e = new ZipEntry(name);
}
e.flag = CENFLG(cen, pos);
e.xdostime = CENTIM(cen, pos);
e.crc = CENCRC(cen, pos);
@ -700,12 +689,17 @@ public class ZipFile implements ZipConstants, Closeable {
e.externalFileAttributes = CENATX_PERMS(cen, pos) & 0xFFFF;
}
int nlen = CENNAM(cen, pos);
int elen = CENEXT(cen, pos);
int clen = CENCOM(cen, pos);
if (elen != 0) {
int start = pos + CENHDR + nlen;
e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true, false);
}
if (clen != 0) {
int start = pos + CENHDR + nlen + elen;
ZipCoder zc = res.zsrc.zipCoderForPos(pos);
e.comment = zc.toString(cen, start, clen);
}
lastEntryName = e.name;
@ -1176,6 +1170,8 @@ public class ZipFile implements ZipConstants, Closeable {
}
);
}
// Represents the resolved name and position of a CEN record
static record EntryPos(String name, int pos) {}
private static class Source {
// While this is only used from ZipFile, defining it there would cause
@ -1849,12 +1845,12 @@ public class ZipFile implements ZipConstants, Closeable {
}
/*
* Returns the {@code pos} of the ZIP cen entry corresponding to the
* specified entry name, or -1 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.
*/
private int getEntryPos(String name, boolean addSlash) {
private EntryPos getEntryPos(String name, boolean addSlash) {
if (total == 0) {
return -1;
return null;
}
int hsh = ZipCoder.hash(name);
@ -1877,7 +1873,7 @@ public class ZipFile implements ZipConstants, Closeable {
switch (zc.compare(name, cen, noff, nlen, addSlash)) {
case EXACT_MATCH:
// We found an exact match for "name"
return pos;
return new EntryPos(name, pos);
case DIRECTORY_MATCH:
// We found the directory "name/"
// Track its position, then continue the search for "name"
@ -1892,10 +1888,10 @@ public class ZipFile implements ZipConstants, Closeable {
// Reaching this point means we did not find "name".
// Return the position of "name/" if we found it
if (dirPos != -1) {
return dirPos;
return new EntryPos(name + "/", dirPos);
}
// No entry found
return -1;
return null;
}
private ZipCoder zipCoderForPos(int pos) {