Fri, 21 Dec 2012 15:27:55 +0000
8003512: javac doesn't work with jar files with >64k entries
Reviewed-by: jjg, ksrini
Contributed-by: martinrb@google.com
1.1 --- a/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java Fri Dec 21 08:45:43 2012 -0800 1.2 +++ b/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java Fri Dec 21 15:27:55 2012 +0000 1.3 @@ -548,17 +548,15 @@ 1.4 } 1.5 1.6 if (i >= 0) { 1.7 - zipDir = new byte[get4ByteLittleEndian(endbuf, i + 12) + 2]; 1.8 - zipDir[0] = endbuf[i + 10]; 1.9 - zipDir[1] = endbuf[i + 11]; 1.10 + zipDir = new byte[get4ByteLittleEndian(endbuf, i + 12)]; 1.11 int sz = get4ByteLittleEndian(endbuf, i + 16); 1.12 // a negative offset or the entries field indicates a 1.13 // potential zip64 archive 1.14 - if (sz < 0 || get2ByteLittleEndian(zipDir, 0) == 0xffff) { 1.15 + if (sz < 0 || get2ByteLittleEndian(endbuf, i + 10) == 0xffff) { 1.16 throw new ZipFormatException("detected a zip64 archive"); 1.17 } 1.18 zipRandomFile.seek(start + sz); 1.19 - zipRandomFile.readFully(zipDir, 2, zipDir.length - 2); 1.20 + zipRandomFile.readFully(zipDir, 0, zipDir.length); 1.21 return; 1.22 } else { 1.23 endbufend = endbufpos + 21; 1.24 @@ -568,14 +566,13 @@ 1.25 } 1.26 1.27 private void buildIndex() throws IOException { 1.28 - int entryCount = get2ByteLittleEndian(zipDir, 0); 1.29 + int len = zipDir.length; 1.30 1.31 // Add each of the files 1.32 - if (entryCount > 0) { 1.33 + if (len > 0) { 1.34 directories = new LinkedHashMap<RelativeDirectory, DirectoryEntry>(); 1.35 ArrayList<Entry> entryList = new ArrayList<Entry>(); 1.36 - int pos = 2; 1.37 - for (int i = 0; i < entryCount; i++) { 1.38 + for (int pos = 0; pos < len; ) { 1.39 pos = readEntry(pos, entryList, directories); 1.40 } 1.41
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/file/zip/8003512/LoadClassFromJava6CreatedJarTest.java Fri Dec 21 15:27:55 2012 +0000 2.3 @@ -0,0 +1,183 @@ 2.4 + 2.5 +/* 2.6 + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. 2.7 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 + * 2.9 + * This code is free software; you can redistribute it and/or modify it 2.10 + * under the terms of the GNU General Public License version 2 only, as 2.11 + * published by the Free Software Foundation. 2.12 + * 2.13 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.16 + * version 2 for more details (a copy is included in the LICENSE file that 2.17 + * accompanied this code). 2.18 + * 2.19 + * You should have received a copy of the GNU General Public License version 2.20 + * 2 along with this work; if not, write to the Free Software Foundation, 2.21 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.22 + * 2.23 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.24 + * or visit www.oracle.com if you need additional information or have any 2.25 + * questions. 2.26 + */ 2.27 + 2.28 +/* 2.29 + * @test 2.30 + * @bug 8003512 2.31 + * @summary javac doesn't work with jar files with >64k entries 2.32 + * @compile -target 6 -source 6 -XDignore.symbol.file LoadClassFromJava6CreatedJarTest.java ../Utils.java 2.33 + * @run main/timeout=360 LoadClassFromJava6CreatedJarTest 2.34 + */ 2.35 + 2.36 +/* 2.37 + * The test creates a jar file with more than 64K entries. The jar file is 2.38 + * created executing the LoadClassFromJava6CreatedJarTest$MakeJar 2.39 + * class with a JVM version 6. The test must include Java 6 features only. 2.40 + * 2.41 + * The aim is to verify classes included in jar files with more than 64K entries 2.42 + * created with Java 6 can be loaded by more recent versions of Java. 2.43 + * 2.44 + * A path to JDK or JRE version 6 is needed. This can be provided 2.45 + * by passing this option to jtreg: 2.46 + * -javaoption:-Djava6.home="/path/to/jdk_or_jre6" 2.47 + */ 2.48 + 2.49 +import java.io.BufferedInputStream; 2.50 +import java.io.BufferedReader; 2.51 +import java.io.File; 2.52 +import java.io.FileInputStream; 2.53 +import java.io.FileOutputStream; 2.54 +import java.io.IOException; 2.55 +import java.io.InputStreamReader; 2.56 +import java.util.Arrays; 2.57 +import java.util.List; 2.58 +import java.util.zip.CRC32; 2.59 +import java.util.zip.ZipEntry; 2.60 +import java.util.zip.ZipOutputStream; 2.61 + 2.62 +public class LoadClassFromJava6CreatedJarTest { 2.63 + 2.64 + static final String javaHome6 = System.getProperty("java6.home"); 2.65 + static final String testClasses = System.getProperty("test.classes"); 2.66 + 2.67 + public static void main(String... args) 2.68 + throws IOException, InterruptedException { 2.69 + if (javaHome6 != null) { 2.70 + new LoadClassFromJava6CreatedJarTest().run(); 2.71 + } else { 2.72 + System.out.println( 2.73 + "The test LoadClassFromJava6CreatedJarTest cannot be executed. " + 2.74 + "In order to run it you should pass an option with " + 2.75 + "this form -javaoption:-Djava6.home=\"/path/to/jdk_or_jre6\" " + 2.76 + "to jtreg."); 2.77 + } 2.78 + } 2.79 + 2.80 + void run() throws IOException, InterruptedException { 2.81 + File classA = new File("A.java"); 2.82 + Utils.createJavaFile(classA, null); 2.83 + if (!Utils.compile("-target", "6", "-source", "6", 2.84 + classA.getAbsolutePath())) { 2.85 + throw new AssertionError("Test failed while compiling class A"); 2.86 + } 2.87 + 2.88 + executeCommand(Arrays.asList(javaHome6 + "/bin/java", "-classpath", 2.89 + testClasses, "LoadClassFromJava6CreatedJarTest$MakeJar")); 2.90 + 2.91 + File classB = new File("B.java"); 2.92 + Utils.createJavaFile(classB, classA); 2.93 + if (!Utils.compile("-cp", "a.jar", classB.getAbsolutePath())) { 2.94 + throw new AssertionError("Test failed while compiling class Main"); 2.95 + } 2.96 + } 2.97 + 2.98 + void executeCommand(List<String> command) 2.99 + throws IOException, InterruptedException { 2.100 + ProcessBuilder pb = new ProcessBuilder(command). 2.101 + redirectErrorStream(true); 2.102 + Process p = pb.start(); 2.103 + BufferedReader r = 2.104 + new BufferedReader(new InputStreamReader(p.getInputStream())); 2.105 + String line; 2.106 + while ((line = r.readLine()) != null) { 2.107 + System.err.println(line); 2.108 + } 2.109 + int rc = p.waitFor(); 2.110 + if (rc != 0) { 2.111 + throw new AssertionError("Unexpected exit code: " + rc); 2.112 + } 2.113 + } 2.114 + 2.115 + static class MakeJar { 2.116 + public static void main(String[] args) throws Throwable { 2.117 + File classFile = new File("A.class"); 2.118 + ZipOutputStream zos = null; 2.119 + FileInputStream fis = null; 2.120 + final int MAX = Short.MAX_VALUE * 2 + 10; 2.121 + ZipEntry ze = null; 2.122 + try { 2.123 + zos = new ZipOutputStream(new FileOutputStream("a.jar")); 2.124 + zos.setLevel(ZipOutputStream.STORED); 2.125 + zos.setMethod(ZipOutputStream.STORED); 2.126 + for (int i = 0; i < MAX ; i++) { 2.127 + ze = new ZipEntry("X" + i + ".txt"); 2.128 + ze.setSize(0); 2.129 + ze.setCompressedSize(0); 2.130 + ze.setCrc(0); 2.131 + zos.putNextEntry(ze); 2.132 + } 2.133 + 2.134 + // add a class file 2.135 + ze = new ZipEntry("A.class"); 2.136 + ze.setCompressedSize(classFile.length()); 2.137 + ze.setSize(classFile.length()); 2.138 + ze.setCrc(computeCRC(classFile)); 2.139 + zos.putNextEntry(ze); 2.140 + fis = new FileInputStream(classFile); 2.141 + for (int c; (c = fis.read()) >= 0;) { 2.142 + zos.write(c); 2.143 + } 2.144 + } finally { 2.145 + zos.close(); 2.146 + fis.close(); 2.147 + } 2.148 + } 2.149 + 2.150 + private static final int BUFFER_LEN = Short.MAX_VALUE * 2; 2.151 + 2.152 + static long getCount(long minlength) { 2.153 + return (minlength / BUFFER_LEN) + 1; 2.154 + } 2.155 + 2.156 + static long computeCRC(long minlength) { 2.157 + CRC32 crc = new CRC32(); 2.158 + byte[] buffer = new byte[BUFFER_LEN]; 2.159 + long count = getCount(minlength); 2.160 + for (long i = 0; i < count; i++) { 2.161 + crc.update(buffer); 2.162 + } 2.163 + return crc.getValue(); 2.164 + } 2.165 + 2.166 + static long computeCRC(File inFile) throws IOException { 2.167 + byte[] buffer = new byte[8192]; 2.168 + CRC32 crc = new CRC32(); 2.169 + FileInputStream fis = null; 2.170 + BufferedInputStream bis = null; 2.171 + try { 2.172 + fis = new FileInputStream(inFile); 2.173 + bis = new BufferedInputStream(fis); 2.174 + int n = bis.read(buffer); 2.175 + while (n > 0) { 2.176 + crc.update(buffer, 0, n); 2.177 + n = bis.read(buffer); 2.178 + } 2.179 + } finally { 2.180 + bis.close(); 2.181 + fis.close(); 2.182 + } 2.183 + return crc.getValue(); 2.184 + } 2.185 + } 2.186 +}
3.1 --- a/test/tools/javac/file/zip/Utils.java Fri Dec 21 08:45:43 2012 -0800 3.2 +++ b/test/tools/javac/file/zip/Utils.java Fri Dec 21 15:27:55 2012 +0000 3.3 @@ -1,5 +1,5 @@ 3.4 /* 3.5 - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3.6 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 3.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.8 * 3.9 * This code is free software; you can redistribute it and/or modify it 3.10 @@ -21,6 +21,12 @@ 3.11 * questions. 3.12 */ 3.13 3.14 +/* 3.15 + * This utils class is been used by test T8003512 which is compiled with Java 6 3.16 + * only features. So if this class is modified, it should be so using Java 6 3.17 + * features only. 3.18 + */ 3.19 + 3.20 import java.io.BufferedInputStream; 3.21 import java.io.BufferedOutputStream; 3.22 import java.io.Closeable;