Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
1917e1ea
Commit
1917e1ea
authored
Mar 22, 2019
by
Camille Vienot
Committed by
Andy Wilkinson
Sep 20, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support zip64 jars
See gh-16091
parent
d5fc3245
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
150 additions
and
17 deletions
+150
-17
CentralDirectoryEndRecord.java
...gframework/boot/loader/jar/CentralDirectoryEndRecord.java
+135
-8
JarFileArchiveTests.java
...ingframework/boot/loader/archive/JarFileArchiveTests.java
+15
-9
No files found.
spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/CentralDirectoryEndRecord.java
View file @
1917e1ea
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
package
org
.
springframework
.
boot
.
loader
.
jar
;
package
org
.
springframework
.
boot
.
loader
.
jar
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.Optional
;
import
org.springframework.boot.loader.data.RandomAccessData
;
import
org.springframework.boot.loader.data.RandomAccessData
;
...
@@ -25,6 +26,7 @@ import org.springframework.boot.loader.data.RandomAccessData;
...
@@ -25,6 +26,7 @@ import org.springframework.boot.loader.data.RandomAccessData;
*
*
* @author Phillip Webb
* @author Phillip Webb
* @author Andy Wilkinson
* @author Andy Wilkinson
* @author Camille Vienot
* @see <a href="https://en.wikipedia.org/wiki/Zip_%28file_format%29">Zip File Format</a>
* @see <a href="https://en.wikipedia.org/wiki/Zip_%28file_format%29">Zip File Format</a>
*/
*/
class
CentralDirectoryEndRecord
{
class
CentralDirectoryEndRecord
{
...
@@ -33,6 +35,8 @@ class CentralDirectoryEndRecord {
...
@@ -33,6 +35,8 @@ class CentralDirectoryEndRecord {
private
static
final
int
MAXIMUM_COMMENT_LENGTH
=
0xFFFF
;
private
static
final
int
MAXIMUM_COMMENT_LENGTH
=
0xFFFF
;
private
static
final
int
ZIP64_MAGICCOUNT
=
0xFFFF
;
private
static
final
int
MAXIMUM_SIZE
=
MINIMUM_SIZE
+
MAXIMUM_COMMENT_LENGTH
;
private
static
final
int
MAXIMUM_SIZE
=
MINIMUM_SIZE
+
MAXIMUM_COMMENT_LENGTH
;
private
static
final
int
SIGNATURE
=
0x06054b50
;
private
static
final
int
SIGNATURE
=
0x06054b50
;
...
@@ -41,6 +45,8 @@ class CentralDirectoryEndRecord {
...
@@ -41,6 +45,8 @@ class CentralDirectoryEndRecord {
private
static
final
int
READ_BLOCK_SIZE
=
256
;
private
static
final
int
READ_BLOCK_SIZE
=
256
;
private
final
Optional
<
Zip64End
>
zip64End
;
private
byte
[]
block
;
private
byte
[]
block
;
private
int
offset
;
private
int
offset
;
...
@@ -69,6 +75,9 @@ class CentralDirectoryEndRecord {
...
@@ -69,6 +75,9 @@ class CentralDirectoryEndRecord {
}
}
this
.
offset
=
this
.
block
.
length
-
this
.
size
;
this
.
offset
=
this
.
block
.
length
-
this
.
size
;
}
}
int
startOfCentralDirectoryEndRecord
=
(
int
)
(
data
.
getSize
()
-
this
.
size
);
this
.
zip64End
=
Optional
.
ofNullable
(
isZip64
()
?
new
Zip64End
(
data
,
startOfCentralDirectoryEndRecord
)
:
null
);
}
}
private
byte
[]
createBlockFromEndOfData
(
RandomAccessData
data
,
int
size
)
throws
IOException
{
private
byte
[]
createBlockFromEndOfData
(
RandomAccessData
data
,
int
size
)
throws
IOException
{
...
@@ -95,7 +104,10 @@ class CentralDirectoryEndRecord {
...
@@ -95,7 +104,10 @@ class CentralDirectoryEndRecord {
long
getStartOfArchive
(
RandomAccessData
data
)
{
long
getStartOfArchive
(
RandomAccessData
data
)
{
long
length
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
12
,
4
);
long
length
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
12
,
4
);
long
specifiedOffset
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
16
,
4
);
long
specifiedOffset
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
16
,
4
);
long
actualOffset
=
data
.
getSize
()
-
this
.
size
-
length
;
long
zip64EndSize
=
this
.
zip64End
.
map
((
x
)
->
x
.
getSize
()).
orElse
(
0L
);
int
zip64LocSize
=
this
.
zip64End
.
map
((
x
)
->
Zip64Locator
.
ZIP64_LOCSIZE
).
orElse
(
0
);
long
actualOffset
=
data
.
getSize
()
-
this
.
size
-
length
-
zip64EndSize
-
zip64LocSize
;
return
actualOffset
-
specifiedOffset
;
return
actualOffset
-
specifiedOffset
;
}
}
...
@@ -106,9 +118,14 @@ class CentralDirectoryEndRecord {
...
@@ -106,9 +118,14 @@ class CentralDirectoryEndRecord {
* @return the central directory data
* @return the central directory data
*/
*/
RandomAccessData
getCentralDirectory
(
RandomAccessData
data
)
{
RandomAccessData
getCentralDirectory
(
RandomAccessData
data
)
{
long
offset
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
16
,
4
);
if
(
isZip64
())
{
long
length
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
12
,
4
);
return
this
.
zip64End
.
get
().
getCentratDirectory
(
data
);
return
data
.
getSubsection
(
offset
,
length
);
}
else
{
long
offset
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
16
,
4
);
long
length
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
12
,
4
);
return
data
.
getSubsection
(
offset
,
length
);
}
}
}
/**
/**
...
@@ -116,11 +133,121 @@ class CentralDirectoryEndRecord {
...
@@ -116,11 +133,121 @@ class CentralDirectoryEndRecord {
* @return the number of records in the zip
* @return the number of records in the zip
*/
*/
int
getNumberOfRecords
()
{
int
getNumberOfRecords
()
{
long
numberOfRecords
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
10
,
2
);
if
(
isZip64
())
{
if
(
numberOfRecords
==
0xFFFF
)
{
return
this
.
zip64End
.
get
().
getNumberOfRecords
();
throw
new
IllegalStateException
(
"Zip64 archives are not supported"
);
}
else
{
long
numberOfRecords
=
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
10
,
2
);
return
(
int
)
numberOfRecords
;
}
}
boolean
isZip64
()
{
return
(
int
)
Bytes
.
littleEndianValue
(
this
.
block
,
this
.
offset
+
10
,
2
)
==
ZIP64_MAGICCOUNT
;
}
/**
* A Zip64 end of central directory record.
*
* @see <a href="https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT">Chapter
* 4.3.14 of Zip64 specification</a>
*/
private
static
class
Zip64End
{
static
final
int
ZIP64_ENDTOT
=
32
;
// total number of entries
static
final
int
ZIP64_ENDSIZ
=
40
;
// central directory size in bytes
static
final
int
ZIP64_ENDOFF
=
48
;
// offset of first CEN header
private
final
Zip64Locator
locator
;
private
final
long
centralDirectoryOffset
;
private
final
long
centralDirectoryLength
;
private
int
numberOfRecords
;
Zip64End
(
RandomAccessData
data
,
int
centratDirectoryEndOffset
)
throws
IOException
{
this
(
data
,
new
Zip64Locator
(
data
,
centratDirectoryEndOffset
));
}
Zip64End
(
RandomAccessData
data
,
Zip64Locator
locator
)
throws
IOException
{
this
.
locator
=
locator
;
byte
[]
block
=
data
.
read
(
locator
.
getZip64EndOffset
(),
56
);
this
.
centralDirectoryOffset
=
Bytes
.
littleEndianValue
(
block
,
ZIP64_ENDOFF
,
8
);
this
.
centralDirectoryLength
=
Bytes
.
littleEndianValue
(
block
,
ZIP64_ENDSIZ
,
8
);
this
.
numberOfRecords
=
(
int
)
Bytes
.
littleEndianValue
(
block
,
ZIP64_ENDTOT
,
8
);
}
}
return
(
int
)
numberOfRecords
;
/**
* Return the size of this zip 64 end of central directory record.
* @return size of this zip 64 end of central directory record
*/
public
long
getSize
()
{
return
this
.
locator
.
getZip64EndSize
();
}
/**
* Return the bytes of the "Central directory" based on the offset indicated in
* this record.
* @param data the source data
* @return the central directory data
*/
public
RandomAccessData
getCentratDirectory
(
RandomAccessData
data
)
{
return
data
.
getSubsection
(
this
.
centralDirectoryOffset
,
this
.
centralDirectoryLength
);
}
/**
* Return the number of entries in the zip64 archive.
* @return the number of records in the zip
*/
public
int
getNumberOfRecords
()
{
return
this
.
numberOfRecords
;
}
}
/**
* A Zip64 end of central directory locator.
*
* @see <a href="https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT">Chapter
* 4.3.15 of Zip64 specification</a>
*/
private
static
class
Zip64Locator
{
static
final
int
ZIP64_LOCSIZE
=
20
;
// locator size
static
final
int
ZIP64_LOCOFF
=
8
;
// offset of zip64 end
private
final
long
zip64EndOffset
;
private
final
int
offset
;
Zip64Locator
(
RandomAccessData
data
,
int
centralDirectoryEndOffset
)
throws
IOException
{
this
.
offset
=
centralDirectoryEndOffset
-
ZIP64_LOCSIZE
;
byte
[]
block
=
data
.
read
(
this
.
offset
,
ZIP64_LOCSIZE
);
this
.
zip64EndOffset
=
Bytes
.
littleEndianValue
(
block
,
ZIP64_LOCOFF
,
8
);
}
/**
* Return the size of the zip 64 end record located by this zip64 end locator.
* @return size of the zip 64 end record located by this zip64 end locator
*/
public
long
getZip64EndSize
()
{
return
this
.
offset
-
this
.
zip64EndOffset
;
}
/**
* Return the offset to locate {@link Zip64End}.
* @return offset of the Zip64 end of central directory record
*/
public
long
getZip64EndOffset
()
{
return
this
.
zip64EndOffset
;
}
}
}
String
getComment
()
{
String
getComment
()
{
...
...
spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/archive/JarFileArchiveTests.java
View file @
1917e1ea
...
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
...
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.net.URL
;
import
java.net.URL
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.Iterator
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.jar.JarEntry
;
import
java.util.jar.JarEntry
;
import
java.util.jar.JarOutputStream
;
import
java.util.jar.JarOutputStream
;
...
@@ -38,13 +39,13 @@ import org.springframework.boot.loader.archive.Archive.Entry;
...
@@ -38,13 +39,13 @@ import org.springframework.boot.loader.archive.Archive.Entry;
import
org.springframework.util.FileCopyUtils
;
import
org.springframework.util.FileCopyUtils
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThatIllegalStateException
;
/**
/**
* Tests for {@link JarFileArchive}.
* Tests for {@link JarFileArchive}.
*
*
* @author Phillip Webb
* @author Phillip Webb
* @author Andy Wilkinson
* @author Andy Wilkinson
* @author Camille Vienot
*/
*/
class
JarFileArchiveTests
{
class
JarFileArchiveTests
{
...
@@ -142,11 +143,15 @@ class JarFileArchiveTests {
...
@@ -142,11 +143,15 @@ class JarFileArchiveTests {
}
}
@Test
@Test
void
zip64ArchivesAreHandledGracefully
()
throws
IOException
{
void
filesInzip64ArchivesAreAllListed
()
throws
IOException
{
File
file
=
new
File
(
this
.
tempDir
,
"test.jar"
);
File
file
=
new
File
(
this
.
tempDir
,
"test.jar"
);
FileCopyUtils
.
copy
(
writeZip64Jar
(),
file
);
FileCopyUtils
.
copy
(
writeZip64Jar
(),
file
);
assertThatIllegalStateException
().
isThrownBy
(()
->
new
JarFileArchive
(
file
))
JarFileArchive
zip64Archive
=
new
JarFileArchive
(
file
);
.
withMessageContaining
(
"Zip64 archives are not supported"
);
Iterator
<
Entry
>
it
=
zip64Archive
.
iterator
();
for
(
int
i
=
0
;
i
<
65537
;
i
++)
{
assertThat
(
it
.
hasNext
()).
as
(
i
+
"nth file is present"
).
isTrue
();
it
.
next
();
}
}
}
@Test
@Test
...
@@ -166,11 +171,12 @@ class JarFileArchiveTests {
...
@@ -166,11 +171,12 @@ class JarFileArchiveTests {
output
.
closeEntry
();
output
.
closeEntry
();
output
.
close
();
output
.
close
();
JarFileArchive
jarFileArchive
=
new
JarFileArchive
(
file
);
JarFileArchive
jarFileArchive
=
new
JarFileArchive
(
file
);
assertThatIllegalStateException
().
isThrownBy
(()
->
{
Archive
nestedArchive
=
jarFileArchive
.
getNestedArchive
(
getEntriesMap
(
jarFileArchive
).
get
(
"nested/zip64.jar"
));
Archive
archive
=
jarFileArchive
.
getNestedArchive
(
getEntriesMap
(
jarFileArchive
).
get
(
"nested/zip64.jar"
));
Iterator
<
Entry
>
it
=
nestedArchive
.
iterator
();
((
JarFileArchive
)
archive
).
close
();
for
(
int
i
=
0
;
i
<
65537
;
i
++)
{
}).
withMessageContaining
(
"Failed to get nested archive for entry nested/zip64.jar"
);
assertThat
(
it
.
hasNext
()).
as
(
i
+
"nth file is present"
).
isTrue
();
jarFileArchive
.
close
();
it
.
next
();
}
}
}
private
byte
[]
writeZip64Jar
()
throws
IOException
{
private
byte
[]
writeZip64Jar
()
throws
IOException
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment