Fix IndexOutOfBoundsException at FastJarFS
If there are more than 0xffff files in the jar, we have to use Zip64 format ^KT-52786 Fixed
This commit is contained in:
committed by
Space Team
parent
fcabc60471
commit
337f461ad8
@@ -28,6 +28,7 @@ class ZipEntryDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const val END_OF_CENTRAL_DIR_SIZE = 22
|
private const val END_OF_CENTRAL_DIR_SIZE = 22
|
||||||
|
private const val END_OF_CENTRAL_DIR_ZIP64_SIZE = 56
|
||||||
private const val LOCAL_FILE_HEADER_EXTRA_OFFSET = 28
|
private const val LOCAL_FILE_HEADER_EXTRA_OFFSET = 28
|
||||||
private const val LOCAL_FILE_HEADER_SIZE = LOCAL_FILE_HEADER_EXTRA_OFFSET + 2
|
private const val LOCAL_FILE_HEADER_SIZE = LOCAL_FILE_HEADER_EXTRA_OFFSET + 2
|
||||||
|
|
||||||
@@ -62,15 +63,7 @@ fun MappedByteBuffer.contentsToByteArray(
|
|||||||
fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
||||||
order(ByteOrder.LITTLE_ENDIAN)
|
order(ByteOrder.LITTLE_ENDIAN)
|
||||||
|
|
||||||
var endOfCentralDirectoryOffset = capacity() - END_OF_CENTRAL_DIR_SIZE
|
val (entriesNumber, offsetOfCentralDirectory) = parseCentralDirectoryRecordsNumberAndOffset()
|
||||||
while (endOfCentralDirectoryOffset >= 0) {
|
|
||||||
// header of "End of central directory"
|
|
||||||
if (getInt(endOfCentralDirectoryOffset) == 0x06054b50) break
|
|
||||||
endOfCentralDirectoryOffset--
|
|
||||||
}
|
|
||||||
|
|
||||||
val entriesNumber = getUnsignedShort(endOfCentralDirectoryOffset + 10)
|
|
||||||
val offsetOfCentralDirectory = getInt(endOfCentralDirectoryOffset + 16)
|
|
||||||
|
|
||||||
var currentOffset = offsetOfCentralDirectory
|
var currentOffset = offsetOfCentralDirectory
|
||||||
|
|
||||||
@@ -129,4 +122,43 @@ fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MappedByteBuffer.parseCentralDirectoryRecordsNumberAndOffset(): Pair<Int, Int> {
|
||||||
|
var endOfCentralDirectoryOffset = capacity() - END_OF_CENTRAL_DIR_SIZE
|
||||||
|
while (endOfCentralDirectoryOffset >= 0) {
|
||||||
|
// header of "End of central directory" (see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
|
||||||
|
if (getInt(endOfCentralDirectoryOffset) == 0x06054b50) break
|
||||||
|
endOfCentralDirectoryOffset--
|
||||||
|
}
|
||||||
|
|
||||||
|
val entriesNumber = getUnsignedShort(endOfCentralDirectoryOffset + 10)
|
||||||
|
val offsetOfCentralDirectory = getInt(endOfCentralDirectoryOffset + 16)
|
||||||
|
// Offset of start of central directory, relative to start of archive (or -1 for ZIP64)
|
||||||
|
// (see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
|
||||||
|
if (entriesNumber == 0xffff || offsetOfCentralDirectory == -1) return parseZip64CentralDirectoryRecordsNumberAndOffset()
|
||||||
|
|
||||||
|
return Pair(entriesNumber, offsetOfCentralDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MappedByteBuffer.parseZip64CentralDirectoryRecordsNumberAndOffset(): Pair<Int, Int> {
|
||||||
|
var endOfCentralDirectoryOffset = capacity() - END_OF_CENTRAL_DIR_ZIP64_SIZE
|
||||||
|
while (endOfCentralDirectoryOffset >= 0) {
|
||||||
|
// header of "End of central directory" (see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
|
||||||
|
if (getInt(endOfCentralDirectoryOffset) == 0x06064b50) break
|
||||||
|
endOfCentralDirectoryOffset--
|
||||||
|
}
|
||||||
|
|
||||||
|
val entriesNumber = getLong(endOfCentralDirectoryOffset + 32)
|
||||||
|
val offsetOfCentralDirectory = getLong(endOfCentralDirectoryOffset + 48)
|
||||||
|
|
||||||
|
require(entriesNumber <= Int.MAX_VALUE) {
|
||||||
|
"Jar $entriesNumber entries number equal or more than ${Int.MAX_VALUE} is not supported by FastJarFS"
|
||||||
|
}
|
||||||
|
|
||||||
|
require(offsetOfCentralDirectory <= Int.MAX_VALUE) {
|
||||||
|
"Jar $offsetOfCentralDirectory offset equal or more than ${Int.MAX_VALUE} is not supported by FastJarFS"
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(entriesNumber.toInt(), offsetOfCentralDirectory.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
private fun ByteBuffer.getUnsignedShort(offset: Int): Int = java.lang.Short.toUnsignedInt(getShort(offset))
|
private fun ByteBuffer.getUnsignedShort(offset: Int): Int = java.lang.Short.toUnsignedInt(getShort(offset))
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.jvm.compiler
|
||||||
|
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import org.junit.Assert
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
|
|
||||||
|
class FastJarFSTest : TestCase() {
|
||||||
|
|
||||||
|
private var fs: FastJarFileSystem? = null
|
||||||
|
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
fs = FastJarFileSystem.createIfUnmappingPossible()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tearDown() {
|
||||||
|
fs?.clearHandlersCache()
|
||||||
|
fs = null
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testZip64FormatIsSupported() {
|
||||||
|
val fs = fs ?: return
|
||||||
|
val tmpDir = KotlinTestUtils.tmpDirForTest(this)
|
||||||
|
val jarFile = File(tmpDir, "tmp.jar")
|
||||||
|
val out = ZipOutputStream(FileOutputStream(jarFile))
|
||||||
|
|
||||||
|
// Should be more than 65535
|
||||||
|
val entriesNumber = 70000
|
||||||
|
for (i in 0..entriesNumber) {
|
||||||
|
out.putNextEntry(ZipEntry("$i.txt"))
|
||||||
|
out.writer().apply {
|
||||||
|
append(i.toString())
|
||||||
|
flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close()
|
||||||
|
|
||||||
|
val indicesToCheck = listOf(
|
||||||
|
0, entriesNumber / 2, entriesNumber / 3, entriesNumber - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for (i in indicesToCheck) {
|
||||||
|
val file = fs.findFileByPath(jarFile.absolutePath + "!/$i.txt") ?: error("Not found $i.txt")
|
||||||
|
Assert.assertEquals(String(file.contentsToByteArray()), i.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user