@@ -90,7 +90,7 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
9090 long len = data .byteSize ();
9191 long centralDirectoryOffsetScanEnd = Math .max (Math .max (precedingEndOfCentralDirectory , 0 ), end .getCentralDirectoryOffset ());
9292 long earliestCentralDirectoryOffset = len - ZipPatterns .CENTRAL_DIRECTORY_FILE_HEADER .length ;
93- long maxRelativeOffset = 0 ;
93+ long maxRelativeLFHOffset = 0 ;
9494 while (true ) {
9595 // Look backwards for the next CEN magic quad.
9696 long offset = MemorySegmentUtil .lastIndexOfQuad (data , earliestCentralDirectoryOffset - 1L , ZipPatterns .CENTRAL_DIRECTORY_FILE_HEADER_QUAD );
@@ -107,6 +107,7 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
107107 // Read central directories going forward from the first CEN header.
108108 // We do this "find the first, then read forward" so that we can skip over data that may
109109 // coincidentally hold the CEN magic, but not actually denote the beginning of a central directory entry.
110+ CentralDirectoryFileHeader latestOffsetCen = null ;
110111 long centralDirectoryOffset = earliestCentralDirectoryOffset ;
111112 while (centralDirectoryOffset >= 0L ) {
112113 CentralDirectoryFileHeader directory = newCentralDirectoryFileHeader ();
@@ -116,9 +117,17 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
116117 // We cannot recover from the CEN reading encountering failures.
117118 throw new IOException (ex );
118119 }
120+
121+ // Add to zip
119122 zip .addPart (directory );
120- if (directory .getRelativeOffsetOfLocalHeader () > maxRelativeOffset )
121- maxRelativeOffset = directory .getRelativeOffsetOfLocalHeader ();
123+
124+ // Track which CEN has the latest offset in the ZIP file
125+ if (directory .getRelativeOffsetOfLocalHeader () > maxRelativeLFHOffset ) {
126+ maxRelativeLFHOffset = directory .getRelativeOffsetOfLocalHeader ();
127+ latestOffsetCen = directory ;
128+ }
129+
130+ // Step forward
122131 centralDirectoryOffset = MemorySegmentUtil .indexOfQuad (data , centralDirectoryOffset + CentralDirectoryFileHeader .MIN_FIXED_SIZE , ZipPatterns .CENTRAL_DIRECTORY_FILE_HEADER_QUAD );
123132 }
124133
@@ -130,14 +139,26 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
130139 if (precedingEndOfCentralDirectory != -1 ) {
131140 // There was a prior end part, so we will seek past it's length and use that as the base offset.
132141 try {
142+ // We roughly compute the "size" of section of data occupied by LocalFileHeader contents.
143+ // The latest relative file offset, plus its header+data size then this should be the section size
144+ // even if we don't know if there is a preceding end header or not to base off of.
145+ if (latestOffsetCen != null ) {
146+ long localFileHeaderSectionLength = LocalFileHeader .MIN_FIXED_SIZE + maxRelativeLFHOffset + latestOffsetCen .getCompressedSize ();
147+ long startOfRelativeLocalFileHeaders = earliestCentralDirectoryOffset - localFileHeaderSectionLength ;
148+ if (precedingEndOfCentralDirectory > startOfRelativeLocalFileHeaders ) {
149+ // This occurs between the start of LocalFileHeader data and our current end and should be ignored.
150+ throw new IllegalStateException ();
151+ }
152+ }
153+
133154 // Make sure it isn't bogus before we use it as a reference point
134155 EndOfCentralDirectory tempEnd = new EndOfCentralDirectory ();
135156 tempEnd .read (data , precedingEndOfCentralDirectory );
136157
137158 // If we use this as a point of reference there must be enough data remaining
138159 // to read the largest offset specified by our central directories.
139160 long hypotheticalJvmBaseOffset = precedingEndOfCentralDirectory + tempEnd .length ();
140- if (len <= hypotheticalJvmBaseOffset + maxRelativeOffset )
161+ if (len <= hypotheticalJvmBaseOffset + maxRelativeLFHOffset )
141162 throw new IllegalStateException ();
142163
143164 // TODO: Double check 'precedingEndOfCentralDirectory' points to a EndOfCentralDirectory that isn't bogus
0 commit comments