-
Bug
-
Resolution: Fixed
-
P3
-
9, 10, 11
-
b129
-
b02
-
generic
-
generic
-
Verified
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8212073 | 11.0.2 | Xueming Shen | P3 | Resolved | Fixed | b01 |
ADDITIONAL SYSTEM INFORMATION :
Macbook Pro 15-inch 2016
Mac OS X High Sierra 10.13.5
OpenJDK jdk-10.0.1.jdk & jdk-11.jdk
A DESCRIPTION OF THE PROBLEM :
I have observed that when building a Jar using JarOutputStream, when adding a JarEntry if I call JarEntry.setLastModifiedTime prior to calling setTime, the Jar is built incorrectly and reading it using java.util.jar.JarFile will throw an exception.
java.util.zip.ZipException: invalid CEN header (bad header size)
Expected Results: Regardless of order, or calls to these methods should produce a valid Jar file or exception while writing the Jar file to indicate the error.
REGRESSION : Last worked in version 8u171
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Open a JarOutputStream
2. Create a single JarEntry
3. setCreationTime
4. setLastAccessTime
5. setLastModifiedTime
6. setTime
7. JarOutputStream.putNextEntry
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expect the Jar to be built correctly or for the JarOutputStream to fail if it is unable to be built correctly.
This previously worked on Oracle JRE jdk1.8.0_171 and earlier.
ACTUAL -
The JarOutputStream does not fail, but the jar is not built correctly, and then reading the jar using java.util.jar.JarFile will throw java.util.zip.ZipException: invalid CEN header (bad header size).
I can recreate these results on Open JDK 10.0.1 and 11.
---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipException;
/**
* @author Daniel DeGroff
*/
public class JarOutputStreamTest {
public static void main(String[] args) throws IOException {
// Create a test file to include in the jar.
Files.deleteIfExists(Paths.get("./foo"));
Path file = Files.createFile(Paths.get("./foo"));
FileTime creationTime = (FileTime) Files.getAttribute(file, "creationTime");
FileTime lastAccessTime = (FileTime) Files.getAttribute(file, "lastAccessTime");
FileTime lastModifiedTime = Files.getLastModifiedTime(file);
File jarFile = new File("testcase");
jarFile.deleteOnExit();
// 1. Passes, order is Ok.
runTestCase(jarFile, file, entry -> {
entry.setCreationTime(creationTime);
entry.setLastAccessTime(lastAccessTime);
// Calling setTime prior to setLastModifiedTime is Ok.
entry.setTime(lastModifiedTime.toMillis());
entry.setLastModifiedTime(lastModifiedTime);
});
// 2. Passes, omit the call to setTime
runTestCase(jarFile, file, entry -> {
entry.setCreationTime(creationTime);
entry.setLastAccessTime(lastAccessTime);
// Omitting the call to setTime is Ok.
entry.setLastModifiedTime(lastModifiedTime);
});
// 3. Passes, omit setCreationTime and setLastAccessTime then order does not matter
runTestCase(jarFile, file, entry -> {
// Calling these two in either order is ok when we don't call setCreationTime and setLastAccessTime
entry.setTime(lastModifiedTime.toMillis());
entry.setLastModifiedTime(lastModifiedTime);
});
// 4. Passes, omit setCreationTime and setLastAccessTime then order does not matter
runTestCase(jarFile, file, entry -> {
// Calling these two in either order is ok when we don't call setCreationTime and setLastAccessTime
entry.setLastModifiedTime(lastModifiedTime);
entry.setTime(lastModifiedTime.toMillis());
});
// 5. Fails
runTestCase(jarFile, file, entry -> {
entry.setCreationTime(creationTime);
entry.setLastAccessTime(lastAccessTime);
// Calling setLastModifiedTime prior to setTime when also calling setCreationTime and setLastAccessTime fails.
entry.setLastModifiedTime(lastModifiedTime);
entry.setTime(lastModifiedTime.toMillis());
});
}
private static void runTestCase(File jarFile, Path file, Consumer<JarEntry> consumer) throws IOException {
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(jarFile.toPath()), new Manifest())) {
JarEntry entry = new JarEntry(file.toString());
consumer.accept(entry);
entry.setSize((Long) Files.getAttribute(file, "size"));
jos.putNextEntry(entry);
jos.flush();
jos.closeEntry();
}
try {
new JarFile(jarFile);
System.out.println("Success!");
} catch (ZipException e) {
// Throws java.util.zip.ZipException: invalid CEN header (bad header size)
System.out.println("Fail. " + e.getClass().getCanonicalName() + ": " + e.getMessage());
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Workaround 1:
Call JarEntry.setTime before JarEntry.setLastModifiedTime
Workaround 2:
Leave setLastModifiedTime before setTime but omit setCreationTime and setLastAccessTime
Workaround 3:
Do not call setTime, instead only call setCreationTime, setLastAccessTime, and setLastModifiedTime
FREQUENCY : always
Macbook Pro 15-inch 2016
Mac OS X High Sierra 10.13.5
OpenJDK jdk-10.0.1.jdk & jdk-11.jdk
A DESCRIPTION OF THE PROBLEM :
I have observed that when building a Jar using JarOutputStream, when adding a JarEntry if I call JarEntry.setLastModifiedTime prior to calling setTime, the Jar is built incorrectly and reading it using java.util.jar.JarFile will throw an exception.
java.util.zip.ZipException: invalid CEN header (bad header size)
Expected Results: Regardless of order, or calls to these methods should produce a valid Jar file or exception while writing the Jar file to indicate the error.
REGRESSION : Last worked in version 8u171
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Open a JarOutputStream
2. Create a single JarEntry
3. setCreationTime
4. setLastAccessTime
5. setLastModifiedTime
6. setTime
7. JarOutputStream.putNextEntry
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expect the Jar to be built correctly or for the JarOutputStream to fail if it is unable to be built correctly.
This previously worked on Oracle JRE jdk1.8.0_171 and earlier.
ACTUAL -
The JarOutputStream does not fail, but the jar is not built correctly, and then reading the jar using java.util.jar.JarFile will throw java.util.zip.ZipException: invalid CEN header (bad header size).
I can recreate these results on Open JDK 10.0.1 and 11.
---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipException;
/**
* @author Daniel DeGroff
*/
public class JarOutputStreamTest {
public static void main(String[] args) throws IOException {
// Create a test file to include in the jar.
Files.deleteIfExists(Paths.get("./foo"));
Path file = Files.createFile(Paths.get("./foo"));
FileTime creationTime = (FileTime) Files.getAttribute(file, "creationTime");
FileTime lastAccessTime = (FileTime) Files.getAttribute(file, "lastAccessTime");
FileTime lastModifiedTime = Files.getLastModifiedTime(file);
File jarFile = new File("testcase");
jarFile.deleteOnExit();
// 1. Passes, order is Ok.
runTestCase(jarFile, file, entry -> {
entry.setCreationTime(creationTime);
entry.setLastAccessTime(lastAccessTime);
// Calling setTime prior to setLastModifiedTime is Ok.
entry.setTime(lastModifiedTime.toMillis());
entry.setLastModifiedTime(lastModifiedTime);
});
// 2. Passes, omit the call to setTime
runTestCase(jarFile, file, entry -> {
entry.setCreationTime(creationTime);
entry.setLastAccessTime(lastAccessTime);
// Omitting the call to setTime is Ok.
entry.setLastModifiedTime(lastModifiedTime);
});
// 3. Passes, omit setCreationTime and setLastAccessTime then order does not matter
runTestCase(jarFile, file, entry -> {
// Calling these two in either order is ok when we don't call setCreationTime and setLastAccessTime
entry.setTime(lastModifiedTime.toMillis());
entry.setLastModifiedTime(lastModifiedTime);
});
// 4. Passes, omit setCreationTime and setLastAccessTime then order does not matter
runTestCase(jarFile, file, entry -> {
// Calling these two in either order is ok when we don't call setCreationTime and setLastAccessTime
entry.setLastModifiedTime(lastModifiedTime);
entry.setTime(lastModifiedTime.toMillis());
});
// 5. Fails
runTestCase(jarFile, file, entry -> {
entry.setCreationTime(creationTime);
entry.setLastAccessTime(lastAccessTime);
// Calling setLastModifiedTime prior to setTime when also calling setCreationTime and setLastAccessTime fails.
entry.setLastModifiedTime(lastModifiedTime);
entry.setTime(lastModifiedTime.toMillis());
});
}
private static void runTestCase(File jarFile, Path file, Consumer<JarEntry> consumer) throws IOException {
try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(jarFile.toPath()), new Manifest())) {
JarEntry entry = new JarEntry(file.toString());
consumer.accept(entry);
entry.setSize((Long) Files.getAttribute(file, "size"));
jos.putNextEntry(entry);
jos.flush();
jos.closeEntry();
}
try {
new JarFile(jarFile);
System.out.println("Success!");
} catch (ZipException e) {
// Throws java.util.zip.ZipException: invalid CEN header (bad header size)
System.out.println("Fail. " + e.getClass().getCanonicalName() + ": " + e.getMessage());
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Workaround 1:
Call JarEntry.setTime before JarEntry.setLastModifiedTime
Workaround 2:
Leave setLastModifiedTime before setTime but omit setCreationTime and setLastAccessTime
Workaround 3:
Do not call setTime, instead only call setCreationTime, setLastAccessTime, and setLastModifiedTime
FREQUENCY : always
- backported by
-
JDK-8212073 JarEntry.setCreation/LastAccessTime without setLastModifiedTime causes Invalid CEN header
-
- Resolved
-