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