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