Skip to content

Commit 7f1dae1

Browse files
author
Eirik Bjørsnøs
committed
8339874: Avoid duplicate checking of trailing slash in ZipFile.getZipEntry
Reviewed-by: lancea, redestad
1 parent 4d65c3e commit 7f1dae1

File tree

2 files changed

+34
-63
lines changed

2 files changed

+34
-63
lines changed

src/java.base/share/classes/java/util/zip/ZipCoder.java

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,6 @@ static int hash(String name) {
158158
return hsh;
159159
}
160160

161-
boolean hasTrailingSlash(byte[] a, int end) {
162-
byte[] slashBytes = slashBytes();
163-
return end >= slashBytes.length &&
164-
Arrays.mismatch(a, end - slashBytes.length, end, slashBytes, 0, slashBytes.length) == -1;
165-
}
166-
167-
private byte[] slashBytes;
168161
private final Charset cs;
169162
protected CharsetDecoder dec;
170163
private CharsetEncoder enc;
@@ -191,23 +184,6 @@ private CharsetEncoder encoder() {
191184
return enc;
192185
}
193186

194-
// This method produces an array with the bytes that will correspond to a
195-
// trailing '/' in the chosen character encoding.
196-
//
197-
// While in most charsets a trailing slash will be encoded as the byte
198-
// value of '/', this does not hold in the general case. E.g., in charsets
199-
// such as UTF-16 and UTF-32 it will be represented by a sequence of 2 or 4
200-
// bytes, respectively.
201-
private byte[] slashBytes() {
202-
if (slashBytes == null) {
203-
// Take into account charsets that produce a BOM, e.g., UTF-16
204-
byte[] slash = "/".getBytes(cs);
205-
byte[] doubleSlash = "//".getBytes(cs);
206-
slashBytes = Arrays.copyOfRange(doubleSlash, slash.length, doubleSlash.length);
207-
}
208-
return slashBytes;
209-
}
210-
211187
/**
212188
* This method is used by ZipFile.Source.getEntryPos when comparing the
213189
* name being looked up to candidate names encoded in the CEN byte
@@ -297,8 +273,7 @@ int checkedHash(byte[] a, int off, int len) throws Exception {
297273
return h;
298274
}
299275

300-
@Override
301-
boolean hasTrailingSlash(byte[] a, int end) {
276+
private boolean hasTrailingSlash(byte[] a, int end) {
302277
return end > 0 && a[end - 1] == '/';
303278
}
304279

src/java.base/share/classes/java/util/zip/ZipFile.java

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,12 @@ public ZipEntry getEntry(String name) {
347347
ZipEntry entry = null;
348348
synchronized (this) {
349349
ensureOpen();
350-
int pos = res.zsrc.getEntryPos(name, true);
351-
if (pos != -1) {
352-
entry = getZipEntry(name, pos);
350+
// Look up the name and CEN header position of the entry.
351+
// The resolved name may include a trailing slash.
352+
// See Source::getEntryPos for details.
353+
EntryPos pos = res.zsrc.getEntryPos(name, true);
354+
if (pos != null) {
355+
entry = getZipEntry(pos.name, pos.pos);
353356
}
354357
}
355358
return entry;
@@ -387,7 +390,12 @@ public InputStream getInputStream(ZipEntry entry) throws IOException {
387390
if (Objects.equals(lastEntryName, entry.name)) {
388391
pos = lastEntryPos;
389392
} else {
390-
pos = zsrc.getEntryPos(entry.name, false);
393+
EntryPos entryPos = zsrc.getEntryPos(entry.name, false);
394+
if (entryPos != null) {
395+
pos = entryPos.pos;
396+
} else {
397+
pos = -1;
398+
}
391399
}
392400
if (pos == -1) {
393401
return null;
@@ -540,7 +548,8 @@ public T next() {
540548
throw new NoSuchElementException();
541549
}
542550
// each "entry" has 3 ints in table entries
543-
return (T)getZipEntry(null, res.zsrc.getEntryPos(i++ * 3));
551+
int pos = res.zsrc.getEntryPos(i++ * 3);
552+
return (T)getZipEntry(getEntryName(pos), pos);
544553
}
545554
}
546555

@@ -612,7 +621,7 @@ public Stream<? extends ZipEntry> stream() {
612621
synchronized (this) {
613622
ensureOpen();
614623
return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
615-
pos -> getZipEntry(null, pos)), false);
624+
pos -> getZipEntry(getEntryName(pos), pos)), false);
616625
}
617626
}
618627

@@ -655,7 +664,7 @@ private Stream<JarEntry> jarStream() {
655664
synchronized (this) {
656665
ensureOpen();
657666
return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
658-
pos -> (JarEntry)getZipEntry(null, pos)), false);
667+
pos -> (JarEntry)getZipEntry(getEntryName(pos), pos)), false);
659668
}
660669
}
661670

@@ -665,30 +674,10 @@ private Stream<JarEntry> jarStream() {
665674
/* Check ensureOpen() before invoking this method */
666675
private ZipEntry getZipEntry(String name, int pos) {
667676
byte[] cen = res.zsrc.cen;
668-
int nlen = CENNAM(cen, pos);
669-
int elen = CENEXT(cen, pos);
670-
int clen = CENCOM(cen, pos);
677+
ZipEntry e = this instanceof JarFile jarFile
678+
? Source.JUJA.entryFor(jarFile, name)
679+
: new ZipEntry(name);
671680

672-
ZipCoder zc = res.zsrc.zipCoderForPos(pos);
673-
if (name != null) {
674-
// only need to check for mismatch of trailing slash
675-
if (nlen > 0 &&
676-
!name.isEmpty() &&
677-
zc.hasTrailingSlash(cen, pos + CENHDR + nlen) &&
678-
!name.endsWith("/"))
679-
{
680-
name += '/';
681-
}
682-
} else {
683-
// invoked from iterator, use the entry name stored in cen
684-
name = zc.toString(cen, pos + CENHDR, nlen);
685-
}
686-
ZipEntry e;
687-
if (this instanceof JarFile) {
688-
e = Source.JUJA.entryFor((JarFile)this, name);
689-
} else {
690-
e = new ZipEntry(name);
691-
}
692681
e.flag = CENFLG(cen, pos);
693682
e.xdostime = CENTIM(cen, pos);
694683
e.crc = CENCRC(cen, pos);
@@ -700,12 +689,17 @@ private ZipEntry getZipEntry(String name, int pos) {
700689
e.externalFileAttributes = CENATX_PERMS(cen, pos) & 0xFFFF;
701690
}
702691

692+
int nlen = CENNAM(cen, pos);
693+
int elen = CENEXT(cen, pos);
694+
int clen = CENCOM(cen, pos);
695+
703696
if (elen != 0) {
704697
int start = pos + CENHDR + nlen;
705698
e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true, false);
706699
}
707700
if (clen != 0) {
708701
int start = pos + CENHDR + nlen + elen;
702+
ZipCoder zc = res.zsrc.zipCoderForPos(pos);
709703
e.comment = zc.toString(cen, start, clen);
710704
}
711705
lastEntryName = e.name;
@@ -1176,6 +1170,8 @@ public void setExternalFileAttributes(ZipEntry ze, int externalFileAttributes) {
11761170
}
11771171
);
11781172
}
1173+
// Represents the resolved name and position of a CEN record
1174+
static record EntryPos(String name, int pos) {}
11791175

11801176
private static class Source {
11811177
// While this is only used from ZipFile, defining it there would cause
@@ -1849,12 +1845,12 @@ private static void zerror(String msg) throws ZipException {
18491845
}
18501846

18511847
/*
1852-
* Returns the {@code pos} of the ZIP cen entry corresponding to the
1853-
* specified entry name, or -1 if not found.
1848+
* Returns the resolved name and position of the ZIP cen entry corresponding
1849+
* to the specified entry name, or {@code null} if not found.
18541850
*/
1855-
private int getEntryPos(String name, boolean addSlash) {
1851+
private EntryPos getEntryPos(String name, boolean addSlash) {
18561852
if (total == 0) {
1857-
return -1;
1853+
return null;
18581854
}
18591855

18601856
int hsh = ZipCoder.hash(name);
@@ -1877,7 +1873,7 @@ private int getEntryPos(String name, boolean addSlash) {
18771873
switch (zc.compare(name, cen, noff, nlen, addSlash)) {
18781874
case EXACT_MATCH:
18791875
// We found an exact match for "name"
1880-
return pos;
1876+
return new EntryPos(name, pos);
18811877
case DIRECTORY_MATCH:
18821878
// We found the directory "name/"
18831879
// Track its position, then continue the search for "name"
@@ -1892,10 +1888,10 @@ private int getEntryPos(String name, boolean addSlash) {
18921888
// Reaching this point means we did not find "name".
18931889
// Return the position of "name/" if we found it
18941890
if (dirPos != -1) {
1895-
return dirPos;
1891+
return new EntryPos(name + "/", dirPos);
18961892
}
18971893
// No entry found
1898-
return -1;
1894+
return null;
18991895
}
19001896

19011897
private ZipCoder zipCoderForPos(int pos) {

0 commit comments

Comments
 (0)