Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static void main(String[] args) throws IOException {
File inputFile = new File(args[0]);
File outputFile = new File(args[1]);

try (ZipReader reader = new ZipReader(inputFile, UTF_8, false);
try (ZipReader reader = ZipReader.createForAdjustSfx(inputFile, UTF_8);
InputStream in = new FileInputStream(inputFile);
OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFile))) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ static ZipFileData read(InputStream in, ZipFileData file) throws IOException {
"Malformed Zip64 End of Central Directory; does not start with %08x", SIGNATURE));
}
file.setZip64(true);
file.setCentralDirectorySize(ZipUtil.getUnsignedLong(fixedSizeData, CD_SIZE_OFFSET));
file.setCentralDirectoryOffset(ZipUtil.getUnsignedLong(fixedSizeData, CD_OFFSET_OFFSET));
file.setExpectedEntries(ZipUtil.getUnsignedLong(fixedSizeData, TOTAL_ENTRIES_OFFSET));
return file;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,22 @@ public ZipReader(File file, Charset charset) throws IOException {
* @throws IOException if an I/O error has occurred
*/
public ZipReader(File file, Charset charset, boolean strictEntries) throws IOException {
this(file, charset, strictEntries, /* isUnadjustedSfx= */ false);
}

ZipReader(File file, Charset charset, boolean strictEntries, boolean isUnadjustedSfx)
throws IOException {
if (file == null || charset == null) {
throw new NullPointerException();
}
this.file = file;
this.in = new RandomAccessFile(file, "r");
this.zipData = new ZipFileData(charset);
readCentralDirectory(strictEntries);
readCentralDirectory(strictEntries, isUnadjustedSfx);
}

static ZipReader createForAdjustSfx(File file, Charset charset) throws IOException {
return new ZipReader(file, charset, /* strictEntries= */ false, /* isUnadjustedSfx= */ true);
}

/**
Expand Down Expand Up @@ -194,7 +203,8 @@ ZipFileData getZipData() {
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
*/
private void readCentralDirectory(boolean strictEntries) throws IOException {
private void readCentralDirectory(boolean strictEntries, boolean isUnadjustedSfx)
throws IOException {
long eocdLocation = findEndOfCentralDirectoryRecord();
InputStream stream = getStreamAt(eocdLocation);
EndOfCentralDirectoryRecord.read(stream, zipData);
Expand All @@ -211,16 +221,21 @@ private void readCentralDirectory(boolean strictEntries) throws IOException {
}
}

long actualCenEnd =
(zipData.isZip64() && zipData.getZip64EndOfCentralDirectoryOffset() > 0)
? zipData.getZip64EndOfCentralDirectoryOffset()
: eocdLocation;
long actualCenStart = actualCenEnd - zipData.getCentralDirectorySize();
if (isUnadjustedSfx) {
long actualCenEnd =
(zipData.isZip64() && zipData.getZip64EndOfCentralDirectoryOffset() > 0)
? zipData.getZip64EndOfCentralDirectoryOffset()
: eocdLocation;
Comment on lines +224 to +228
long actualCenStart = actualCenEnd - zipData.getCentralDirectorySize();
readCentralDirectoryFileHeaders(zipData.getExpectedEntries(), actualCenStart);
return;
}

if (zipData.isZip64() || strictEntries) {
// If in Zip64 format or using strict entry numbers, use the parsed information as is to read
// the central directory file headers.
readCentralDirectoryFileHeaders(zipData.getExpectedEntries(), actualCenStart);
readCentralDirectoryFileHeaders(
zipData.getExpectedEntries(), zipData.getCentralDirectoryOffset());
} else {
// If not in Zip64 format, compute central directory offset by end of central directory record
// offset and central directory size to allow reading large non-compliant Zip32 directories.
Expand All @@ -230,7 +245,8 @@ private void readCentralDirectory(boolean strictEntries) throws IOException {
if ((int) centralDirectoryOffset == (int) zipData.getCentralDirectoryOffset()) {
readCentralDirectoryFileHeaders(centralDirectoryOffset);
} else {
readCentralDirectoryFileHeaders(zipData.getExpectedEntries(), actualCenStart);
readCentralDirectoryFileHeaders(
zipData.getExpectedEntries(), zipData.getCentralDirectoryOffset());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,32 @@ private void assertDateAboutNow(Date testDate) {
}
}

@Test
public void testZip64_WithZip32Offsets() throws IOException {
// A zip file with Zip64 EOCD and Locator, but with non-overflowing 32-bit values in EOCD (as generated by Python / standard tools for archives with Zip64 entries).
byte[] data = new byte[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This huge array will be unpleasant to maintain, but it's fine for now.

0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x74, 0x65,
0x73, 0x74, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x74, 0x65, 0x73, 0x74, 0x50, 0x4b, 0x06, 0x06, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2d, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x07,
0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00
};
try (FileOutputStream out = new FileOutputStream(test)) {
out.write(data);
}
try (ZipReader reader = new ZipReader(test, UTF_8)) {
assertThat(reader.entries()).hasSize(1);
assertThat(reader.getEntry("test")).isNotNull();
}
}

@Test public void testZip64_Potential() throws IOException {
try (ZipWriter writer = new ZipWriter(new FileOutputStream(test), UTF_8, true)) {
ZipFileEntry template = new ZipFileEntry("template");
Expand Down Expand Up @@ -612,7 +638,7 @@ public void testEocdSignatureNearBufferBoundary() throws IOException {
// Initializing ZipReader parses the EOCD header. This verifies that signatureLocation + 20
// falling across the 64-byte buffer boundary is handled gracefully without throwing an
// ArrayIndexOutOfBoundsException.
try (ZipReader reader = new ZipReader(sfxFile, UTF_8)) {
try (ZipReader reader = ZipReader.createForAdjustSfx(sfxFile, UTF_8)) {
assertThat(reader.entries()).hasSize(1);
ZipFileEntry entry = reader.getEntry("foo.txt");
assertThat(entry).isNotNull();
Expand Down