Skip to content

Commit 21fd572

Browse files
committed
Improve JvmZipReader handling of CEN magic matches that don't denote an actual CEN entry
Reading backwards, then forwards allows us to skip over the full CEN entry range, which can include CEN magic headers as part of their fields.
1 parent ed6d0c2 commit 21fd572

File tree

2 files changed

+32
-18
lines changed

2 files changed

+32
-18
lines changed

src/main/java/software/coley/lljzip/format/model/CentralDirectoryFileHeader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
* @author Matt Coley
3737
*/
3838
public class CentralDirectoryFileHeader extends AbstractZipFileHeader {
39-
protected static final long MIN_FIXED_SIZE = 46;
39+
public static final long MIN_FIXED_SIZE = 46;
4040

4141
private transient LocalFileHeader linkedFileHeader;
4242

src/main/java/software/coley/lljzip/format/read/JvmZipReader.java

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,26 +86,40 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
8686
end.read(data, endOfCentralDirectoryOffset);
8787
zip.addPart(end);
8888

89-
// Read central directories (going from the back to the front) up until the preceding ZIP file (if any)
90-
// but not surpassing the declared cen directory offset in the end of central directory header.
89+
// Find the first CEN header.
9190
long len = data.byteSize();
92-
long centralDirectoryOffset = len - ZipPatterns.CENTRAL_DIRECTORY_FILE_HEADER.length;
93-
long maxRelativeOffset = 0;
9491
long centralDirectoryOffsetScanEnd = Math.max(Math.max(precedingEndOfCentralDirectory, 0), end.getCentralDirectoryOffset());
95-
while (centralDirectoryOffset > centralDirectoryOffsetScanEnd) {
96-
centralDirectoryOffset = MemorySegmentUtil.lastIndexOfQuad(data, centralDirectoryOffset - 1L, ZipPatterns.CENTRAL_DIRECTORY_FILE_HEADER_QUAD);
97-
if (centralDirectoryOffset >= 0L) {
98-
CentralDirectoryFileHeader directory = newCentralDirectoryFileHeader();
99-
try {
100-
directory.read(data, centralDirectoryOffset);
101-
} catch (ZipParseException ex) {
102-
// We cannot recover from the CEN reading encountering failures.
103-
throw new IOException(ex);
104-
}
105-
zip.addPart(directory);
106-
if (directory.getRelativeOffsetOfLocalHeader() > maxRelativeOffset)
107-
maxRelativeOffset = directory.getRelativeOffsetOfLocalHeader();
92+
long earliestCentralDirectoryOffset = len - ZipPatterns.CENTRAL_DIRECTORY_FILE_HEADER.length;
93+
long maxRelativeOffset = 0;
94+
while (true) {
95+
// Look backwards for the next CEN magic quad.
96+
long offset = MemorySegmentUtil.lastIndexOfQuad(data, earliestCentralDirectoryOffset - 1L, ZipPatterns.CENTRAL_DIRECTORY_FILE_HEADER_QUAD);
97+
98+
// If the offset is before the END defined CEN offset, then we should stop scanning.
99+
// This offset is the first CEN for the given END.
100+
if (offset < centralDirectoryOffsetScanEnd)
101+
break;
102+
103+
// Record as the new earliest CEN offset, and loop again.
104+
earliestCentralDirectoryOffset = offset;
105+
}
106+
107+
// Read central directories going forward from the first CEN header.
108+
// We do this "find the first, then read forward" so that we can skip over data that may
109+
// coincidentally hold the CEN magic, but not actually denote the beginning of a central directory entry.
110+
long centralDirectoryOffset = earliestCentralDirectoryOffset;
111+
while (centralDirectoryOffset >= 0L) {
112+
CentralDirectoryFileHeader directory = newCentralDirectoryFileHeader();
113+
try {
114+
directory.read(data, centralDirectoryOffset);
115+
} catch (ZipParseException ex) {
116+
// We cannot recover from the CEN reading encountering failures.
117+
throw new IOException(ex);
108118
}
119+
zip.addPart(directory);
120+
if (directory.getRelativeOffsetOfLocalHeader() > maxRelativeOffset)
121+
maxRelativeOffset = directory.getRelativeOffsetOfLocalHeader();
122+
centralDirectoryOffset = MemorySegmentUtil.indexOfQuad(data, centralDirectoryOffset + CentralDirectoryFileHeader.MIN_FIXED_SIZE, ZipPatterns.CENTRAL_DIRECTORY_FILE_HEADER_QUAD);
109123
}
110124

111125
// Determine base offset for computing file header locations with.

0 commit comments

Comments
 (0)