1 /*
   2  * Copyright (c) 2013 Google Inc. 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  * @summary Test java.util.zip behavior with ~64k entries
  27  * @library /test/lib
  28  * @run main/othervm EntryCount64k
  29  * @run main/othervm -Djdk.util.zip.inhibitZip64=true EntryCount64k
  30  * @run main/othervm -Djdk.util.zip.inhibitZip64=false EntryCount64k
  31  */
  32 
  33 import java.io.BufferedInputStream;
  34 import java.io.BufferedOutputStream;
  35 import java.io.File;
  36 import java.nio.file.Files;
  37 import java.io.FileInputStream;
  38 import java.io.FileOutputStream;
  39 import java.io.RandomAccessFile;
  40 import java.nio.file.Paths;
  41 import java.util.Enumeration;
  42 import java.util.zip.ZipEntry;
  43 import java.util.zip.ZipFile;
  44 import java.util.zip.ZipInputStream;
  45 import java.util.zip.ZipOutputStream;
  46 
  47 import jdk.test.lib.process.OutputAnalyzer;
  48 import jdk.test.lib.process.ProcessTools;
  49 
  50 public class EntryCount64k {
  51     public static class Main {
  52         public static void main(String[] args) {
  53             System.out.print("Main");
  54         }
  55     }
  56 
  57     static final String MAIN_CLASS = "EntryCount64k$Main";
  58     static final String THIS_CLASS = "EntryCount64k";
  59     static final String[] SPECIAL_CLASSES = { MAIN_CLASS, THIS_CLASS };
  60     // static final String[] SPECIAL_CLASSES = { MAIN_CLASS };
  61     static final int SPECIAL_COUNT = 1 + SPECIAL_CLASSES.length;
  62 
  63     public static void main(String[] args) throws Throwable {
  64         for (int i = (1 << 16) - 3; i < (1 << 16) + 2; i++)
  65             test(i);
  66     }
  67 
  68     static void test(int entryCount) throws Throwable {
  69         File zipFile = new File("EntryCount64k-tmp.zip");
  70         zipFile.delete();
  71 
  72         try (FileOutputStream fos = new FileOutputStream(zipFile);
  73              BufferedOutputStream bos = new BufferedOutputStream(fos);
  74              ZipOutputStream zos = new ZipOutputStream(bos)) {
  75 
  76             // Add entries to allow the zip file to be used with "java -jar"
  77             zos.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
  78             for (String line : new String[] {
  79                      "Manifest-Version: 1.0",
  80                      "Main-Class: " + MAIN_CLASS,
  81                  })
  82                 zos.write((line + "\n").getBytes("US-ASCII"));
  83             zos.closeEntry();
  84 
  85             String testClasses = System.getProperty("test.classes");
  86             for (String className : SPECIAL_CLASSES) {
  87                 String baseName = className + ".class";
  88                 ZipEntry ze = new ZipEntry(baseName);
  89                 File file = new File(testClasses, baseName);
  90                 zos.putNextEntry(ze);
  91                 Files.copy(file.toPath(), zos);
  92                 zos.closeEntry();
  93             }
  94 
  95             for (int i = SPECIAL_COUNT; i < entryCount; i++) {
  96                 zos.putNextEntry(new ZipEntry(Integer.toString(i)));
  97                 zos.closeEntry();
  98             }
  99         }
 100 
 101         String p = System.getProperty("jdk.util.zip.inhibitZip64");
 102         boolean tooManyEntries = entryCount >= (1 << 16) - 1;
 103         boolean shouldUseZip64 = tooManyEntries & !("true".equals(p));
 104         boolean usesZip64 = usesZip64(zipFile);
 105         String details = String.format
 106             ("entryCount=%d shouldUseZip64=%s usesZip64=%s zipSize=%d%n",
 107              entryCount, shouldUseZip64, usesZip64, zipFile.length());
 108         System.err.println(details);
 109         checkCanRead(zipFile, entryCount);
 110         if (shouldUseZip64 != usesZip64)
 111             throw new Error(details);
 112         zipFile.delete();
 113     }
 114 
 115     static boolean usesZip64(File zipFile) throws Exception {
 116         RandomAccessFile raf = new RandomAccessFile(zipFile, "r");
 117         byte[] buf = new byte[4096];
 118         raf.seek(raf.length() - buf.length);
 119         raf.read(buf);
 120         for (int i = 0; i < buf.length - 4; i++) {
 121             // Look for ZIP64 End Header Signature
 122             // Phil Katz: yes, we will always remember you
 123             if (buf[i+0] == 'P' &&
 124                 buf[i+1] == 'K' &&
 125                 buf[i+2] == 6   &&
 126                 buf[i+3] == 6)
 127                 return true;
 128         }
 129         return false;
 130     }
 131 
 132     static void checkCanRead(File zipFile, int entryCount) throws Throwable {
 133         // Check ZipInputStream API
 134         try (FileInputStream fis = new FileInputStream(zipFile);
 135              BufferedInputStream bis = new BufferedInputStream(fis);
 136              ZipInputStream zis = new ZipInputStream(bis)) {
 137             for (int i = 0; i < entryCount; i++) {
 138                 ZipEntry e = zis.getNextEntry();
 139                 if (i >= SPECIAL_COUNT) // skip special entries
 140                     if (Integer.parseInt(e.getName()) != i)
 141                         throw new AssertionError(e.getName());
 142             }
 143             if (zis.getNextEntry() != null)
 144                 throw new AssertionError();
 145         }
 146 
 147         // Check ZipFile API
 148         try (ZipFile zf = new ZipFile(zipFile)) {
 149             Enumeration<? extends ZipEntry> en = zf.entries();
 150             for (int i = 0; i < entryCount; i++) {
 151                 ZipEntry e = en.nextElement();
 152                 if (i >= SPECIAL_COUNT) // skip special entries
 153                     if (Integer.parseInt(e.getName()) != i)
 154                         throw new AssertionError();
 155             }
 156             if (en.hasMoreElements()
 157                 || (zf.size() != entryCount)
 158                 || (zf.getEntry(Integer.toString(entryCount - 1)) == null)
 159                 || (zf.getEntry(Integer.toString(entryCount)) != null))
 160                 throw new AssertionError();
 161         }
 162 
 163         // Check java -jar
 164         String javaHome = System.getProperty("java.home");
 165         String java = Paths.get(javaHome, "bin", "java").toString();
 166         String[] cmd = { java, "-jar", zipFile.getName() };
 167         ProcessBuilder pb = new ProcessBuilder(cmd);
 168         OutputAnalyzer a = ProcessTools.executeProcess(pb);
 169         a.shouldHaveExitValue(0);
 170         a.stdoutShouldMatch("\\AMain\\Z");
 171         a.stderrShouldMatch("\\A\\Z");
 172     }
 173 }