1/*
2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 6836682 7025988
27 * @summary JavacFileManager handling of zip64 archives (Scenario A and B)
28 * @modules jdk.compiler
29 *          jdk.jartool/sun.tools.jar
30 * @compile  -XDignore.symbol.file T6836682.java Utils.java
31 * @run main T6836682
32 */
33/*
34 * This test consists of two scenarios:
35 *
36 * Scenario A: create a jar with entries exceeding 64K, and see if the javac
37 * can handle this large jar on the classpath. Generally this test completes
38 * within a minute
39 *
40 * Scenario B: create a jar with a large enough file exceeding 4GB, and
41 * similarly test javac. This test is known to be slow and problematic on
42 * certain operating systems, thus this test can be selected by passing a
43 * property through jtreg as follows:
44 * -javaoptions=-DT6836682.testScenarioB=true.
45 * Note this test will only run iff all the disk requirements are met at runtime.
46 */
47import java.io.BufferedInputStream;
48import java.io.BufferedOutputStream;
49import java.io.File;
50import java.io.FileInputStream;
51import java.io.FileOutputStream;
52import java.io.IOException;
53import java.io.OutputStream;
54import java.nio.file.Files;
55import java.nio.file.Path;
56import java.util.zip.CRC32;
57import java.util.zip.ZipEntry;
58import java.util.zip.ZipOutputStream;
59
60public class T6836682 {
61
62    private static final long GIGA = 1024 * 1024 * 1024;
63    private static final int BUFFER_LEN = Short.MAX_VALUE * 2;
64
65    static long getCount(long minlength) {
66        return (minlength / BUFFER_LEN) + 1;
67    }
68
69    static long computeCRC(long minlength) {
70        CRC32 crc = new CRC32();
71        byte[] buffer = new byte[BUFFER_LEN];
72        long count = getCount(minlength);
73        for (long i = 0; i < count; i++) {
74            crc.update(buffer);
75        }
76        return crc.getValue();
77    }
78
79    static long computeCRC(File inFile) throws IOException {
80        byte[] buffer = new byte[8192];
81        CRC32 crc = new CRC32();
82        FileInputStream fis = null;
83        BufferedInputStream bis = null;
84        try {
85            fis = new FileInputStream(inFile);
86            bis = new BufferedInputStream(fis);
87            int n = bis.read(buffer);
88            while (n > 0) {
89                crc.update(buffer, 0, n);
90                n = bis.read(buffer);
91            }
92        } finally {
93            Utils.close(bis);
94            Utils.close(fis);
95        }
96        return crc.getValue();
97    }
98
99    static void createLargeFile(OutputStream os, long minlength) throws IOException {
100        byte[] buffer = new byte[BUFFER_LEN];
101        long count = getCount(minlength);
102        for (long i = 0; i < count; i++) {
103            os.write(buffer);
104        }
105        os.flush();
106    }
107
108    static void createJarWithLargeFile(File jarFile, File javaFile,
109            long minlength) throws IOException {
110        Utils.createClassFile(javaFile, null, true);
111        File classFile = new File(Utils.getClassFileName(javaFile));
112        ZipOutputStream zos = null;
113        BufferedOutputStream bos = null;
114        FileInputStream fis = null;
115        try {
116            zos = new ZipOutputStream(new FileOutputStream(jarFile));
117            zos.setLevel(ZipOutputStream.STORED);
118            zos.setMethod(0);
119            bos = new BufferedOutputStream(zos);
120
121            ZipEntry ze = new ZipEntry("large.data");
122            ze.setCompressedSize(getCount(minlength) * BUFFER_LEN);
123            ze.setSize(getCount(minlength) * BUFFER_LEN);
124            ze.setCrc(computeCRC(minlength));
125            ze.setMethod(ZipEntry.STORED);
126            zos.putNextEntry(ze);
127            createLargeFile(bos, minlength);
128
129            ze = new ZipEntry(classFile.getName());
130            ze.setCompressedSize(classFile.length());
131            ze.setSize(classFile.length());
132            ze.setCrc(computeCRC(classFile));
133            ze.setMethod(ZipEntry.STORED);
134            zos.putNextEntry(ze);
135            fis = new FileInputStream(classFile);
136            Utils.copyStream(fis, bos);
137            bos.flush();
138            zos.closeEntry();
139        } finally {
140            Utils.close(bos);
141            Utils.close(zos);
142            Utils.close(fis);
143        }
144        // deleted to prevent accidental linkage
145        new File(Utils.getClassFileName(javaFile)).delete();
146    }
147
148    static void createLargeJar(File jarFile, File javaFile) throws IOException {
149        File classFile = new File(Utils.getClassFileName(javaFile));
150        Utils.createClassFile(javaFile, null, true);
151        ZipOutputStream zos = null;
152        FileInputStream fis = null;
153        final int MAX = Short.MAX_VALUE * 2 + 10;
154        ZipEntry ze = null;
155        try {
156            zos = new ZipOutputStream(new FileOutputStream(jarFile));
157            zos.setLevel(ZipOutputStream.STORED);
158            zos.setMethod(ZipOutputStream.STORED);
159            for (int i = 0; i < MAX ; i++) {
160                ze = new ZipEntry("X" + i + ".txt");
161                ze.setSize(0);
162                ze.setCompressedSize(0);
163                ze.setCrc(0);
164                zos.putNextEntry(ze);
165            }
166
167            // add a class file
168            ze = new ZipEntry(classFile.getName());
169            ze.setCompressedSize(classFile.length());
170            ze.setSize(classFile.length());
171            ze.setCrc(computeCRC(classFile));
172            zos.putNextEntry(ze);
173            fis = new FileInputStream(classFile);
174            Utils.copyStream(fis, zos);
175        } finally {
176            Utils.close(zos);
177            Utils.close(fis);
178        // deleted to prevent accidental linkage
179        new File(Utils.getClassFileName(javaFile)).delete();
180    }
181    }
182
183    // a jar with entries exceeding 64k + a class file for the existential test
184    public static void testScenarioA(String... args) throws IOException {
185        File largeJar = new File("large.jar");
186        File javaFile = new File("Foo.java");
187        createLargeJar(largeJar, javaFile);
188
189        File testFile = new File("Bar.java");
190        try {
191            Utils.createJavaFile(testFile, javaFile);
192            if (!Utils.compile("-doe", "-verbose", "-cp",
193                    largeJar.getAbsolutePath(), testFile.getAbsolutePath())) {
194                throw new IOException("test failed");
195            }
196        } finally {
197            Utils.deleteFile(largeJar);
198        }
199    }
200
201    // a jar with an enormous file + a class file for the existential test
202    public static void testScenarioB(String... args) throws IOException {
203        final File largeJar = new File("huge.jar");
204        final File javaFile = new File("Foo.java");
205
206        final Path path = largeJar.getAbsoluteFile().getParentFile().toPath();
207        final long available = Files.getFileStore(path).getUsableSpace();
208        final long MAX_VALUE = 0xFFFF_FFFFL;
209
210        final long absolute  = MAX_VALUE + 1L;
211        final long required  = (long)(absolute * 1.1); // pad for sundries
212        System.out.println("\tavailable: " + available / GIGA + " GB");
213        System.out.println("\trequired: " + required / GIGA + " GB");
214
215        if (available > required) {
216            createJarWithLargeFile(largeJar, javaFile, absolute);
217            File testFile = new File("Bar.java");
218            Utils.createJavaFile(testFile, javaFile);
219            try {
220                if (!Utils.compile("-doe", "-verbose", "-cp",
221                        largeJar.getAbsolutePath(), testFile.getAbsolutePath())) {
222                    throw new IOException("test failed");
223                }
224            } finally {
225                Utils.deleteFile(largeJar);
226            }
227        } else {
228            System.out.println("Warning: testScenarioB passes vacuously," +
229                    " requirements exceeds available space");
230        }
231    }
232
233    public static void main(String... args) throws IOException {
234        testScenarioA();
235        System.out.println("testScenarioA: PASS");
236        if (Boolean.getBoolean("T6836682.testScenarioB")) {
237            testScenarioB();
238            System.out.println("testScenarioB: PASS");
239        } else {
240            System.out.println("Warning: testScenarioB, large file test skipped");
241        }
242    }
243}
244