Bug in Oracle's JDK zip filesystem, how do you write an SSCCE to reproduce it?
Asked Answered
B

1

8

This bug is present in the latest 1.7 and 1.8 versions of the JDK (7u72, 8u25). Required: jackson-databind 2.5.0. Tested on Linux x86_64 (Ubuntu 14.10 to be precise).

Code:

public static void main(final String... args)
    throws IOException
{
    final Map<String, String> map
        = Collections.singletonMap("create", "true");
    final Path zipfile = Paths.get("/tmp/foo.zip");
    Files.deleteIfExists(zipfile);
    final URI uri = URI.create("jar:" + zipfile.toUri());
    final ObjectMapper mapper = new ObjectMapper();

    try (
        final FileSystem zipfs = FileSystems.newFileSystem(uri, map);
        final OutputStream out
            = Files.newOutputStream(zipfs.getPath("/t.json"));
    ) {
        mapper.writeValue(out, "hello");
    }
}

This produces an invalid zip file:

$ unzip /tmp/foo.zip 
Archive:  /tmp/foo.zip
replace t.json? [y]es, [n]o, [A]ll, [N]one, [r]ename: A
  inflating: t.json                  
  error:  invalid compressed data to inflate

I have originally opened the bug on Jackson's issue tracker even though it really isn't the culprit here, and a solution was found to work around it: disable JsonGenerator.Feature.AUTO_CLOSE_SOURCE in the ObjectMapper. This option which is enabled by default tells the mapper to close the stream.

While I'd like to open the bug to Oracle, I'd first like to be able to write an SSCCE, but I can't. I have tried to close the stream twice (since it is closed twice in the example), not to use a try-with-resources statement etc... To no avail.

Can you come up with an SSCCE for this problem?

Backscratcher answered 14/1, 2015 at 23:50 Comment(10)
I might look into the size of what's written to the zip entry. And take a very close look at the source to figure out possible places that it could break.Negotiation
Also, try creating an OutputStream decorator that throws when close() is called twice.Negotiation
Why isn't Jackson the cuplrit?Cruciate
@EJP well, as I see it, since there's no I/O error at all, the fs should not have created a corrupted zip in the first placeBackscratcher
I suspect Jackson is the culprit, specifically its com.fasterxml.jackson.core.io.UTF8Writer class, since replacing OutputStream out with Writer out = Files.newBufferedWriter results in a valid zip file.Retroact
@Retroact I don't really agree; it is up to the filesystem to ensure that the streams it provides cannot result in corrupt files (provided that there is no exception of course)Backscratcher
When I write the bytes directly (without using ObjectMapper) and add an explicit close() call, I get the same invalid zip file, using 1.7.0_72-b14 in Linux. I don't know if that's helpful information.Retroact
@Retroact how do you write? I have tried a few ways of provoking this bug but could never achieve this :/Backscratcher
Inside the try block, I do out.write("\"hello\"".getBytes(StandardCharsets.US_ASCII)); followed by out.close();.Retroact
@Retroact would you mind writing an answer? I confirm that you are right, it does reproduce the bug each timeBackscratcher
R
3

I had thought Jackson was doing something untoward, but it turns out one can reproduce the problem without any Jackson code. I replaced the body of the try block with two lines that (I'm pretty sure) do the same thing, and the result is still an invalid zip file:

try (
    final FileSystem zipfs = FileSystems.newFileSystem(uri, map);
    final OutputStream out
        = Files.newOutputStream(zipfs.getPath("/t.json"));
) {
    out.write("\"hello\"".getBytes(StandardCharsets.US_ASCII));
    out.close();
}
Retroact answered 16/1, 2015 at 17:54 Comment(1)
FWIW: bug is opened. Thanks again for the example code!Backscratcher

© 2022 - 2024 — McMap. All rights reserved.