# HG changeset patch # User sherman # Date 1505950914 25200 # Wed Sep 20 16:41:54 2017 -0700 # Node ID 723486922bfe4c17e3f5c067ce5e97229842fbcd # Parent c8ac05bbe47771b3dafa2e7fc9a95d86d68d7c07 8186464: ZipFile cannot read some InfoZip ZIP64 zip files Reviewed-by: martin diff --git openjdk.orig/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java openjdk/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java index 26e2a5bf9e9..2630c118817 100644 --- openjdk.orig/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java +++ openjdk/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java @@ -92,6 +92,7 @@ public class ZipFileSystem extends FileSystem { private final boolean createNew; // create a new zip if not exists private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows"); + private final boolean forceEnd64; // a threshold, in bytes, to decide whether to create a temp file // for outputstream of a zip entry @@ -112,12 +113,13 @@ public class ZipFileSystem extends FileSystem { if (this.defaultDir.charAt(0) != '/') throw new IllegalArgumentException("default dir should be absolute"); + this.forceEnd64 = "true".equals(env.get("forceZIP64End")); this.provider = provider; this.zfpath = zfpath; if (Files.notExists(zfpath)) { if (createNew) { try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) { - new END().write(os, 0); + new END().write(os, 0, forceEnd64); } } else { throw new FileSystemNotFoundException(zfpath.toString()); @@ -1014,28 +1016,36 @@ public class ZipFileSystem extends FileSystem { end.cenoff = ENDOFF(buf); end.comlen = ENDCOM(buf); end.endpos = pos + i; - if (end.cenlen == ZIP64_MINVAL || - end.cenoff == ZIP64_MINVAL || - end.centot == ZIP64_MINVAL32) - { - // need to find the zip64 end; - byte[] loc64 = new byte[ZIP64_LOCHDR]; - if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) - != loc64.length) { - return end; - } - long end64pos = ZIP64_LOCOFF(loc64); - byte[] end64buf = new byte[ZIP64_ENDHDR]; - if (readFullyAt(end64buf, 0, end64buf.length, end64pos) - != end64buf.length) { - return end; - } - // end64 found, re-calcualte everything. - end.cenlen = ZIP64_ENDSIZ(end64buf); - end.cenoff = ZIP64_ENDOFF(end64buf); - end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g - end.endpos = end64pos; + // try if there is zip64 end; + byte[] loc64 = new byte[ZIP64_LOCHDR]; + if (end.endpos < ZIP64_LOCHDR || + readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) + != loc64.length || + !locator64SigAt(loc64, 0)) { + return end; + } + long end64pos = ZIP64_LOCOFF(loc64); + byte[] end64buf = new byte[ZIP64_ENDHDR]; + if (readFullyAt(end64buf, 0, end64buf.length, end64pos) + != end64buf.length || + !end64SigAt(end64buf, 0)) { + return end; + } + // end64 found, + long cenlen64 = ZIP64_ENDSIZ(end64buf); + long cenoff64 = ZIP64_ENDOFF(end64buf); + long centot64 = ZIP64_ENDTOT(end64buf); + // double-check + if (cenlen64 != end.cenlen && end.cenlen != ZIP64_MINVAL || + cenoff64 != end.cenoff && end.cenoff != ZIP64_MINVAL || + centot64 != end.centot && end.centot != ZIP64_MINVAL32) { + return end; } + // to use the end64 values + end.cenlen = cenlen64; + end.cenoff = cenoff64; + end.centot = (int)centot64; // assume total < 2g + end.endpos = end64pos; return end; } } @@ -1201,7 +1211,7 @@ public class ZipFileSystem extends FileSystem { // sync the zip file system, if there is any udpate private void sync() throws IOException { - //System.out.printf("->sync(%s) starting....!%n", toString()); + // System.out.printf("->sync(%s) starting....!%n", toString()); // check ex-closer if (!exChClosers.isEmpty()) { for (ExChannelCloser ecc : exChClosers) { @@ -1292,7 +1302,7 @@ public class ZipFileSystem extends FileSystem { } end.centot = elist.size(); end.cenlen = written - end.cenoff; - end.write(os, written); + end.write(os, written, forceEnd64); } if (!streams.isEmpty()) { // @@ -1849,8 +1859,8 @@ public class ZipFileSystem extends FileSystem { long endpos; int disktot; - void write(OutputStream os, long offset) throws IOException { - boolean hasZip64 = false; + void write(OutputStream os, long offset, boolean forceEnd64) throws IOException { + boolean hasZip64 = forceEnd64; // false; long xlen = cenlen; long xoff = cenoff; if (xlen >= ZIP64_MINVAL) { @@ -1875,8 +1885,8 @@ public class ZipFileSystem extends FileSystem { writeShort(os, 45); // version needed to extract writeInt(os, 0); // number of this disk writeInt(os, 0); // central directory start disk - writeLong(os, centot); // number of directory entires on disk - writeLong(os, centot); // number of directory entires + writeLong(os, centot); // number of directory entries on disk + writeLong(os, centot); // number of directory entries writeLong(os, cenlen); // length of central directory writeLong(os, cenoff); // offset of central directory diff --git openjdk.orig/jdk/src/share/native/java/util/zip/zip_util.c openjdk/jdk/src/share/native/java/util/zip/zip_util.c index 5fd6fea049d..858e5814e92 100644 --- openjdk.orig/jdk/src/share/native/java/util/zip/zip_util.c +++ openjdk/jdk/src/share/native/java/util/zip/zip_util.c @@ -385,6 +385,9 @@ findEND64(jzfile *zip, void *end64buf, jlong endpos) { char loc64[ZIP64_LOCHDR]; jlong end64pos; + if (endpos < ZIP64_LOCHDR) { + return -1; + } if (readFullyAt(zip->zfd, loc64, ZIP64_LOCHDR, endpos - ZIP64_LOCHDR) == -1) { return -1; // end64 locator not found } @@ -567,6 +570,7 @@ readCEN(jzfile *zip, jint knownTotal) { /* Following are unsigned 32-bit */ jlong endpos, end64pos, cenpos, cenlen, cenoff; + jlong cenlen64, cenoff64, centot64; /* Following are unsigned 16-bit */ jint total, tablelen, i, j; unsigned char *cenbuf = NULL; @@ -594,13 +598,20 @@ readCEN(jzfile *zip, jint knownTotal) cenlen = ENDSIZ(endbuf); cenoff = ENDOFF(endbuf); total = ENDTOT(endbuf); - if (cenlen == ZIP64_MAGICVAL || cenoff == ZIP64_MAGICVAL || - total == ZIP64_MAGICCOUNT) { - unsigned char end64buf[ZIP64_ENDHDR]; - if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) { - cenlen = ZIP64_ENDSIZ(end64buf); - cenoff = ZIP64_ENDOFF(end64buf); - total = (jint)ZIP64_ENDTOT(end64buf); + unsigned char end64buf[ZIP64_ENDHDR]; + if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) { + // end64 candidate found, + cenlen64 = ZIP64_ENDSIZ(end64buf); + cenoff64 = ZIP64_ENDOFF(end64buf); + centot64 = ZIP64_ENDTOT(end64buf); + // double-check + if ((cenlen64 == cenlen || cenlen == ZIP64_MAGICVAL) && + (cenoff64 == cenoff || cenoff == ZIP64_MAGICVAL) && + (centot64 == total || total == ZIP64_MAGICCOUNT)) { + // to use the end64 values + cenlen = cenlen64; + cenoff = cenoff64; + total = (jint)centot64; endpos = end64pos; endhdrlen = ZIP64_ENDHDR; } diff --git openjdk.orig/jdk/test/java/util/zip/ZipFile/ReadZip.java openjdk/jdk/test/java/util/zip/ZipFile/ReadZip.java index ffe8a8ed712..9b380003893 100644 --- openjdk.orig/jdk/test/java/util/zip/ZipFile/ReadZip.java +++ openjdk/jdk/test/java/util/zip/ZipFile/ReadZip.java @@ -22,7 +22,7 @@ */ /* @test - * @bug 4241361 4842702 4985614 6646605 5032358 6923692 6233323 8144977 8184993 + * @bug 4241361 4842702 4985614 6646605 5032358 6923692 6233323 8144977 8184993 8186464 * @summary Make sure we can read a zip file. @key randomness * @run main/othervm ReadZip @@ -31,12 +31,24 @@ */ import java.io.*; +import java.net.URI; import java.nio.file.Files; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.zip.*; +import sun.misc.IOUtils; + +import static java.nio.charset.StandardCharsets.US_ASCII; + public class ReadZip { private static void unreached (Object o) throws Exception @@ -144,8 +156,6 @@ public class ReadZip { newZip.delete(); } - - // Throw a FNF exception when read a non-existing zip file try { unreached (new ZipFile( new File(System.getProperty("test.src", "."), @@ -153,5 +163,54 @@ public class ReadZip { + String.valueOf(new java.util.Random().nextInt()) + ".zip"))); } catch (FileNotFoundException fnfe) {} + + // read a zip file with ZIP64 end + Path path = Paths.get(System.getProperty("test.dir", ""), "end64.zip"); + try { + URI uri = URI.create("jar:" + path.toUri()); + Map env = new HashMap<>(); + env.put("create", "true"); + env.put("forceZIP64End", "true"); + try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { + Files.write(fs.getPath("hello"), "hello".getBytes()); + } + try (ZipFile zf = new ZipFile(path.toFile())) { + if (!"hello".equals(new String(IOUtils.readAllBytes(zf.getInputStream(new ZipEntry("hello"))), + US_ASCII))) + throw new RuntimeException("zipfile: read entry failed"); + } catch (IOException x) { + throw new RuntimeException("zipfile: zip64 end failed"); + } + try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + if (!"hello".equals(new String(Files.readAllBytes(fs.getPath("hello"))))) + throw new RuntimeException("zipfs: read entry failed"); + } catch (IOException x) { + throw new RuntimeException("zipfile: zip64 end failed"); + } + } finally { + Files.deleteIfExists(path); + } + + // read a zip file created via "echo hello | zip dst.zip -", which uses + // ZIP64 end record + if (Files.notExists(Paths.get("/usr/bin/zip"))) + return; + try { + Process zip = new ProcessBuilder("zip", path.toString().toString(), "-").start(); + OutputStream os = zip.getOutputStream(); + os.write("hello".getBytes(US_ASCII)); + os.close(); + zip.waitFor(); + if (zip.exitValue() == 0 && Files.exists(path)) { + try (ZipFile zf = new ZipFile(path.toFile())) { + if (!"hello".equals(new String(IOUtils.readAllBytes(zf.getInputStream(new ZipEntry("-")))))) + throw new RuntimeException("zipfile: read entry failed"); + } catch (IOException x) { + throw new RuntimeException("zipfile: zip64 end failed"); + } + } + } finally { + Files.deleteIfExists(path); + } } }