1 /*
   2  * Copyright (c) 2007, 2016, 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 import java.nio.file.Path;
  25 import java.io.BufferedReader;
  26 import java.io.ByteArrayOutputStream;
  27 import java.io.Closeable;
  28 import java.io.File;
  29 import java.io.FileFilter;
  30 import java.io.FileOutputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.InputStreamReader;
  34 import java.io.PrintStream;
  35 import java.net.URI;
  36 import java.net.URL;
  37 import java.nio.charset.Charset;
  38 import java.nio.file.attribute.BasicFileAttributes;
  39 import java.nio.file.FileSystem;
  40 import java.nio.file.FileSystems;
  41 import java.nio.file.FileVisitResult;
  42 import java.nio.file.FileVisitor;
  43 import java.nio.file.Files;
  44 import java.util.ArrayList;
  45 import java.util.Arrays;
  46 import java.util.Collections;
  47 import java.util.List;
  48 import java.util.Map;
  49 import java.util.HashMap;
  50 import java.util.jar.JarFile;
  51 import java.util.jar.JarOutputStream;
  52 import java.util.jar.Pack200;
  53 import java.util.regex.Matcher;
  54 import java.util.regex.Pattern;
  55 import java.util.zip.ZipEntry;
  56 import java.util.zip.ZipFile;
  57 
  58 import static java.nio.file.StandardCopyOption.*;
  59 import static java.nio.file.StandardOpenOption.*;
  60 
  61 
  62 /**
  63  *
  64  * @author ksrini
  65  */
  66 
  67 /*
  68  * This class contains the commonly used utilities.
  69  */
  70 class Utils {
  71     static final String JavaHome = System.getProperty("test.java",
  72             System.getProperty("java.home"));
  73     static final boolean IsWindows =
  74             System.getProperty("os.name").startsWith("Windows");
  75     static final boolean Is64Bit =
  76             System.getProperty("sun.arch.data.model", "32").equals("64");
  77     static final File   JavaSDK =  new File(JavaHome);
  78 
  79     static final String PACK_FILE_EXT   = ".pack";
  80     static final String JAVA_FILE_EXT   = ".java";
  81     static final String CLASS_FILE_EXT  = ".class";
  82     static final String JAR_FILE_EXT    = ".jar";
  83 
  84     static final File   TEST_SRC_DIR = new File(System.getProperty("test.src"));
  85     static final File   TEST_CLS_DIR = new File(System.getProperty("test.classes"));
  86     static final String VERIFIER_DIR_NAME = "pack200-verifier";
  87     static final File   VerifierJar = new File(VERIFIER_DIR_NAME + JAR_FILE_EXT);
  88     static final File   XCLASSES = new File("xclasses");
  89 
  90     private Utils() {} // all static
  91 
  92     private static void init() throws IOException {
  93         if (VerifierJar.exists()) {
  94             return;
  95         }
  96         File srcDir = new File(getVerifierDir(), "src");
  97         List<File> javaFileList = findFiles(srcDir, createFilter(JAVA_FILE_EXT));
  98         File tmpFile = File.createTempFile("javac", ".tmp");
  99         XCLASSES.mkdirs();
 100         FileOutputStream fos = null;
 101         PrintStream ps = null;
 102         try {
 103             fos = new FileOutputStream(tmpFile);
 104             ps = new PrintStream(fos);
 105             for (File f : javaFileList) {
 106                 ps.println(f.getAbsolutePath());
 107             }
 108         } finally {
 109             close(ps);
 110             close(fos);
 111         }
 112 
 113         compiler("-d",
 114                 XCLASSES.getName(),
 115                 "-XaddExports:jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED",
 116                 "@" + tmpFile.getAbsolutePath());
 117 
 118         jar("cvfe",
 119             VerifierJar.getName(),
 120             "sun.tools.pack.verify.Main",
 121             "-C",
 122             XCLASSES.getName(),
 123             ".");
 124     }
 125 
 126     private static File getVerifierDir() {
 127         File srcDir = new File(TEST_SRC_DIR, VERIFIER_DIR_NAME);
 128         if (!srcDir.exists()) {
 129             // if not available try one level above
 130             srcDir = new File(TEST_SRC_DIR.getParentFile(), VERIFIER_DIR_NAME);
 131         }
 132         return srcDir;
 133     }
 134 
 135     static File getGoldenJar() {
 136         return new File(new File(getVerifierDir(), "data"), "golden.jar");
 137     }
 138     static void dirlist(File dir) {
 139         File[] files = dir.listFiles();
 140         System.out.println("--listing " + dir.getAbsolutePath() + "---");
 141         for (File f : files) {
 142             StringBuffer sb = new StringBuffer();
 143             sb.append(f.isDirectory() ? "d " : "- ");
 144             sb.append(f.getName());
 145             System.out.println(sb);
 146         }
 147     }
 148     static void doCompareVerify(File reference, File specimen) throws IOException {
 149         init();
 150         List<String> cmds = new ArrayList<String>();
 151         cmds.add(getJavaCmd());
 152         cmds.add("-XaddExports:jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED");
 153         cmds.add("-cp");
 154         cmds.add(VerifierJar.getName());
 155         cmds.add("sun.tools.pack.verify.Main");
 156         cmds.add(reference.getAbsolutePath());
 157         cmds.add(specimen.getAbsolutePath());
 158         cmds.add("-O");
 159         runExec(cmds);
 160     }
 161 
 162     static void doCompareBitWise(File reference, File specimen)
 163             throws IOException {
 164         init();
 165         List<String> cmds = new ArrayList<String>();
 166         cmds.add(getJavaCmd());
 167         cmds.add("-cp");
 168         cmds.add(VerifierJar.getName());
 169         cmds.add("sun.tools.pack.verify.Main");
 170         cmds.add(reference.getName());
 171         cmds.add(specimen.getName());
 172         cmds.add("-O");
 173         cmds.add("-b");
 174         runExec(cmds);
 175     }
 176 
 177     static FileFilter createFilter(final String extension) {
 178         return new FileFilter() {
 179             @Override
 180             public boolean accept(File pathname) {
 181                 String name = pathname.getName();
 182                 if (name.endsWith(extension)) {
 183                     return true;
 184                 }
 185                 return false;
 186             }
 187         };
 188     }
 189 
 190     /*
 191      * clean up all the usual suspects
 192      */
 193     static void cleanup() throws IOException {
 194         recursiveDelete(XCLASSES);
 195         List<File> toDelete = new ArrayList<>();
 196         toDelete.addAll(Utils.findFiles(new File("."),
 197                 Utils.createFilter(".out")));
 198         toDelete.addAll(Utils.findFiles(new File("."),
 199                 Utils.createFilter(".bak")));
 200         toDelete.addAll(Utils.findFiles(new File("."),
 201                 Utils.createFilter(".jar")));
 202         toDelete.addAll(Utils.findFiles(new File("."),
 203                 Utils.createFilter(".pack")));
 204         toDelete.addAll(Utils.findFiles(new File("."),
 205                 Utils.createFilter(".bnd")));
 206         toDelete.addAll(Utils.findFiles(new File("."),
 207                 Utils.createFilter(".txt")));
 208         toDelete.addAll(Utils.findFiles(new File("."),
 209                 Utils.createFilter(".idx")));
 210         toDelete.addAll(Utils.findFiles(new File("."),
 211                 Utils.createFilter(".gidx")));
 212         for (File f : toDelete) {
 213             f.delete();
 214         }
 215     }
 216 
 217     static final FileFilter DIR_FILTER = new FileFilter() {
 218         public boolean accept(File pathname) {
 219             if (pathname.isDirectory()) {
 220                 return true;
 221             }
 222             return false;
 223         }
 224     };
 225 
 226     static final FileFilter FILE_FILTER = new FileFilter() {
 227         public boolean accept(File pathname) {
 228             if (pathname.isFile()) {
 229                 return true;
 230             }
 231             return false;
 232         }
 233     };
 234 
 235     static void copyFile(File src, File dst) throws IOException {
 236         Path parent = dst.toPath().getParent();
 237         if (parent != null) {
 238             Files.createDirectories(parent);
 239         }
 240         Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING);
 241         if (dst.isDirectory() && !dst.canWrite()) {
 242             dst.setWritable(true);
 243         }
 244     }
 245 
 246     static String baseName(File file, String extension) {
 247         return baseName(file.getAbsolutePath(), extension);
 248     }
 249 
 250     static String baseName(String name, String extension) {
 251         int cut = name.length() - extension.length();
 252         return name.lastIndexOf(extension) == cut
 253                 ? name.substring(0, cut)
 254                 : name;
 255 
 256     }
 257    static void createFile(File outFile, List<String> content) throws IOException {
 258         Files.write(outFile.getAbsoluteFile().toPath(), content,
 259                 Charset.defaultCharset(), CREATE_NEW, TRUNCATE_EXISTING);
 260     }
 261 
 262     /*
 263      * Suppose a path is provided which consists of a full path
 264      * this method returns the sub path for a full path ex: /foo/bar/baz/foobar.z
 265      * and the base path is /foo/bar it will will return baz/foobar.z.
 266      */
 267     private static String getEntryPath(String basePath, String fullPath) {
 268         if (!fullPath.startsWith(basePath)) {
 269             return null;
 270         }
 271         return fullPath.substring(basePath.length());
 272     }
 273 
 274     static String getEntryPath(File basePathFile, File fullPathFile) {
 275         return getEntryPath(basePathFile.toString(), fullPathFile.toString());
 276     }
 277 
 278     public static void recursiveCopy(File src, File dest) throws IOException {
 279         if (!src.exists() || !src.canRead()) {
 280             throw new IOException("file not found or readable: " + src);
 281         }
 282         if (dest.exists() && !dest.isDirectory() && !dest.canWrite()) {
 283             throw new IOException("file not found or writeable: " + dest);
 284         }
 285         if (!dest.exists()) {
 286             dest.mkdirs();
 287         }
 288         List<File> a = directoryList(src);
 289         for (File f : a) {
 290             copyFile(f, new File(dest, getEntryPath(src, f)));
 291         }
 292     }
 293 
 294     static List<File> directoryList(File dirname) {
 295         List<File>  dirList = new ArrayList<File>();
 296         return directoryList(dirname, dirList, null);
 297     }
 298 
 299     private static List<File> directoryList(File dirname, List<File> dirList,
 300             File[] dirs) {
 301         dirList.addAll(Arrays.asList(dirname.listFiles(FILE_FILTER)));
 302         dirs = dirname.listFiles(DIR_FILTER);
 303         for (File f : dirs) {
 304             if (f.isDirectory() && !f.equals(dirname)) {
 305                 dirList.add(f);
 306                 directoryList(f, dirList, dirs);
 307             }
 308         }
 309         return dirList;
 310     }
 311 
 312     static void recursiveDelete(File dir) throws IOException {
 313         if (dir.isFile()) {
 314             dir.delete();
 315         } else if (dir.isDirectory()) {
 316             File[] entries = dir.listFiles();
 317             for (int i = 0; i < entries.length; i++) {
 318                 if (entries[i].isDirectory()) {
 319                     recursiveDelete(entries[i]);
 320                 }
 321                 entries[i].delete();
 322             }
 323             dir.delete();
 324         }
 325     }
 326 
 327     static List<File> findFiles(File startDir, FileFilter filter)
 328             throws IOException {
 329         List<File> list = new ArrayList<File>();
 330         findFiles0(startDir, list, filter);
 331         return list;
 332     }
 333     /*
 334      * finds files in the start directory using the the filter, appends
 335      * the files to the dirList.
 336      */
 337     private static void findFiles0(File startDir, List<File> list,
 338                                     FileFilter filter) throws IOException {
 339         File[] foundFiles = startDir.listFiles(filter);
 340         if (foundFiles == null) {
 341             return;
 342         }
 343         list.addAll(Arrays.asList(foundFiles));
 344         File[] dirs = startDir.listFiles(DIR_FILTER);
 345         for (File dir : dirs) {
 346             findFiles0(dir, list, filter);
 347         }
 348     }
 349 
 350     static void close(Closeable c) {
 351         if (c == null) {
 352             return;
 353         }
 354         try {
 355             c.close();
 356         } catch (IOException ignore) {
 357         }
 358     }
 359 
 360     static void compiler(String... javacCmds) {
 361         List<String> cmdList = new ArrayList<>();
 362         cmdList.add(getJavacCmd());
 363         for (String x : javacCmds) {
 364             cmdList.add(x);
 365         }
 366         runExec(cmdList);
 367     }
 368 
 369     static void jar(String... jargs) {
 370         List<String> cmdList = new ArrayList<>();
 371         cmdList.add(getJarCmd());
 372         for (String x : jargs) {
 373             cmdList.add(x);
 374         }
 375         runExec(cmdList);
 376     }
 377 
 378     static void testWithRepack(File inFile, String... repackOpts) throws IOException {
 379         File cwd = new File(".");
 380         // pack using --repack in native mode
 381         File nativejarFile = new File(cwd, "out-n" + Utils.JAR_FILE_EXT);
 382         repack(inFile, nativejarFile, false, repackOpts);
 383         doCompareVerify(inFile, nativejarFile);
 384 
 385         // ensure bit compatibility between the unpacker variants
 386         File javajarFile = new File(cwd, "out-j" + Utils.JAR_FILE_EXT);
 387         repack(inFile, javajarFile, true, repackOpts);
 388         doCompareBitWise(javajarFile, nativejarFile);
 389     }
 390 
 391     static List<String> repack(File inFile, File outFile,
 392             boolean disableNative, String... extraOpts) {
 393         List<String> cmdList = new ArrayList<>();
 394         cmdList.clear();
 395         cmdList.add(Utils.getJavaCmd());
 396         cmdList.add("-ea");
 397         cmdList.add("-esa");
 398         if (disableNative) {
 399             cmdList.add("-Dcom.sun.java.util.jar.pack.disable.native=true");
 400         }
 401         cmdList.add("com.sun.java.util.jar.pack.Driver");
 402         cmdList.add("--repack");
 403         if (extraOpts != null) {
 404            for (String opt: extraOpts) {
 405                cmdList.add(opt);
 406            }
 407         }
 408         cmdList.add(outFile.getName());
 409         cmdList.add(inFile.getName());
 410         return Utils.runExec(cmdList);
 411     }
 412 
 413     // given a jar file foo.jar will write to foo.pack
 414     static void pack(JarFile jarFile, File packFile) throws IOException {
 415         Pack200.Packer packer = Pack200.newPacker();
 416         Map<String, String> p = packer.properties();
 417         // Take the time optimization vs. space
 418         p.put(packer.EFFORT, "1");  // CAUTION: do not use 0.
 419         // Make the memory consumption as effective as possible
 420         p.put(packer.SEGMENT_LIMIT, "10000");
 421         // ignore all JAR deflation requests to save time
 422         p.put(packer.DEFLATE_HINT, packer.FALSE);
 423         // save the file ordering of the original JAR
 424         p.put(packer.KEEP_FILE_ORDER, packer.TRUE);
 425         FileOutputStream fos = null;
 426         try {
 427             // Write out to a jtreg scratch area
 428             fos = new FileOutputStream(packFile);
 429             // Call the packer
 430             packer.pack(jarFile, fos);
 431         } finally {
 432             close(fos);
 433         }
 434     }
 435 
 436     // uses java unpacker, slow but useful to discover issues with the packer
 437     static void unpackj(File inFile, JarOutputStream jarStream)
 438             throws IOException {
 439         unpack0(inFile, jarStream, true);
 440 
 441     }
 442 
 443     // uses native unpacker using the java APIs
 444     static void unpackn(File inFile, JarOutputStream jarStream)
 445             throws IOException {
 446         unpack0(inFile, jarStream, false);
 447     }
 448 
 449     // given a packed file, create the jar file in the current directory.
 450     private static void unpack0(File inFile, JarOutputStream jarStream,
 451             boolean useJavaUnpack) throws IOException {
 452         // Unpack the files
 453         Pack200.Unpacker unpacker = Pack200.newUnpacker();
 454         Map<String, String> props = unpacker.properties();
 455         if (useJavaUnpack) {
 456             props.put("com.sun.java.util.jar.pack.disable.native", "true");
 457         }
 458         // Call the unpacker
 459         unpacker.unpack(inFile, jarStream);
 460     }
 461 
 462     static byte[] getBuffer(ZipFile zf, ZipEntry ze) throws IOException {
 463         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 464         byte buf[] = new byte[8192];
 465         InputStream is = null;
 466         try {
 467             is = zf.getInputStream(ze);
 468             int n = is.read(buf);
 469             while (n > 0) {
 470                 baos.write(buf, 0, n);
 471                 n = is.read(buf);
 472             }
 473             return baos.toByteArray();
 474         } finally {
 475             close(is);
 476         }
 477     }
 478 
 479     static ArrayList<String> getZipFileEntryNames(ZipFile z) {
 480         ArrayList<String> out = new ArrayList<String>();
 481         for (ZipEntry ze : Collections.list(z.entries())) {
 482             out.add(ze.getName());
 483         }
 484         return out;
 485     }
 486     static List<String> runExec(List<String> cmdsList) {
 487         return runExec(cmdsList, null);
 488     }
 489     static List<String> runExec(List<String> cmdsList, Map<String, String> penv) {
 490         ArrayList<String> alist = new ArrayList<String>();
 491         ProcessBuilder pb =
 492                 new ProcessBuilder(cmdsList);
 493         Map<String, String> env = pb.environment();
 494         if (penv != null && !penv.isEmpty()) {
 495             env.putAll(penv);
 496         }
 497         pb.directory(new File("."));
 498         dirlist(new File("."));
 499         for (String x : cmdsList) {
 500             System.out.print(x + " ");
 501         }
 502         System.out.println("");
 503         int retval = 0;
 504         Process p = null;
 505         InputStreamReader ir = null;
 506         BufferedReader rd = null;
 507         InputStream is = null;
 508         try {
 509             pb.redirectErrorStream(true);
 510             p = pb.start();
 511             is = p.getInputStream();
 512             ir = new InputStreamReader(is);
 513             rd = new BufferedReader(ir, 8192);
 514 
 515             String in = rd.readLine();
 516             while (in != null) {
 517                 alist.add(in);
 518                 System.out.println(in);
 519                 in = rd.readLine();
 520             }
 521             retval = p.waitFor();
 522             if (retval != 0) {
 523                 throw new RuntimeException("process failed with non-zero exit");
 524             }
 525         } catch (Exception ex) {
 526             throw new RuntimeException(ex.getMessage());
 527         } finally {
 528             close(rd);
 529             close(ir);
 530             close(is);
 531             if (p != null) {
 532                 p.destroy();
 533             }
 534         }
 535         return alist;
 536     }
 537 
 538     static String getUnpack200Cmd() {
 539         return getAjavaCmd("unpack200");
 540     }
 541 
 542     static String getPack200Cmd() {
 543         return getAjavaCmd("pack200");
 544     }
 545 
 546     static String getJavaCmd() {
 547         return getAjavaCmd("java");
 548     }
 549 
 550     static String getJavacCmd() {
 551         return getAjavaCmd("javac");
 552     }
 553 
 554     static String getJarCmd() {
 555         return getAjavaCmd("jar");
 556     }
 557 
 558     static String getAjavaCmd(String cmdStr) {
 559         File binDir = new File(JavaHome, "bin");
 560         File unpack200File = IsWindows
 561                 ? new File(binDir, cmdStr + ".exe")
 562                 : new File(binDir, cmdStr);
 563 
 564         String cmd = unpack200File.getAbsolutePath();
 565         if (!unpack200File.canExecute()) {
 566             throw new RuntimeException("please check" +
 567                     cmd + " exists and is executable");
 568         }
 569         return cmd;
 570     }
 571 
 572     // used to get all classes
 573     static File createRtJar() throws Exception {
 574         File rtJar = new File("rt.jar");
 575         new JrtToZip(".*\\.class", rtJar).run();
 576         return rtJar;
 577     }
 578 
 579     // used to select the contents
 580     static File createRtJar(String pattern) throws Exception {
 581         File rtJar = new File("rt.jar");
 582         new JrtToZip(pattern, rtJar).run();
 583         return rtJar;
 584     }
 585 
 586     /*
 587      * A helper class to create a pseudo rt.jar.
 588      */
 589     static class JrtToZip {
 590 
 591         final File outFile;
 592         final Pattern pattern;
 593 
 594         public static void main(String[] args) throws Exception {
 595             new JrtToZip(args[0], new File(args[1])).run();
 596         }
 597 
 598         JrtToZip(String pattern, File outFile) throws Exception {
 599             this.pattern = Pattern.compile(pattern);
 600             this.outFile = outFile;
 601         }
 602 
 603         void run() throws Exception {
 604             URI uri = URI.create("jar:file:///" + outFile.getAbsolutePath().replace("\\", "/"));
 605             Map<String, String> env = new HashMap<>();
 606             env.put("create", "true");
 607             try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
 608                 toZipfs(zipfs);
 609             }
 610         }
 611 
 612         void toZipfs(FileSystem zipfs) throws Exception {
 613             FileSystem jrtfs = FileSystems.getFileSystem(new URL("jrt:/").toURI());
 614             for (Path p : jrtfs.getRootDirectories()) {
 615                 Files.walkFileTree(p, new FileVisitor<Path>() {
 616                     @Override
 617                     public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
 618                         // ignore unneeded directory
 619                         if (dir.startsWith("/packages"))
 620                             return FileVisitResult.SKIP_SUBTREE;
 621 
 622                         // pre-create required directories
 623                         Path zpath = zipfs.getPath(dir.toString());
 624                         Files.createDirectories(zpath);
 625                         return FileVisitResult.CONTINUE;
 626                     }
 627 
 628                     @Override
 629                     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
 630                         Matcher matcher = pattern.matcher(file.toString());
 631                         if (matcher.matches()) {
 632                             // System.out.println("x: " + file);
 633                             Path zpath = zipfs.getPath(file.toString());
 634                             Files.copy(file, zpath, REPLACE_EXISTING);
 635                         }
 636                         return FileVisitResult.CONTINUE;
 637                     }
 638 
 639                     @Override
 640                     public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
 641                         return FileVisitResult.CONTINUE;
 642                     }
 643 
 644                     @Override
 645                     public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
 646                         return FileVisitResult.CONTINUE;
 647                     }
 648                 });
 649             }
 650         }
 651     }
 652 }