1 /*
   2  * Copyright (c) 2010, 2011, 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  * @summary test module file hashing.
  27  */
  28 
  29 import java.io.*;
  30 import java.security.*;
  31 import java.util.*;
  32 import org.openjdk.jigsaw.*;
  33 import org.openjdk.jigsaw.cli.*;
  34 
  35 public class ModuleFormatHeaderHashTest {
  36     final String MNAME = "hello";
  37     final String MVER = "0.1";
  38     String moduleinfo = "module " + MNAME + " @ " + MVER + " {}";
  39 
  40     public static void main(String[] args) throws Exception {
  41         new ModuleFormatHeaderHashTest().run();
  42     }
  43 
  44     void run() throws Exception {
  45         try {
  46             test();
  47         } catch (Throwable t) {
  48             t.printStackTrace();
  49             errors++;
  50         }
  51 
  52 
  53         if (errors == 0)
  54             System.out.println(count + " tests passed");
  55         else
  56             throw new Exception(errors + "/" + count + " tests failed");
  57     }
  58 
  59     void testEmptyModule() throws Exception {
  60         System.err.println("Test: Empty module");
  61         count++;
  62         reset();
  63         List<File> files = new ArrayList<File>();
  64         addFile(files, createFile("module-info.java", moduleinfo));
  65         compile(files);
  66         compress(MNAME);
  67         byte [] expected = readHash(MNAME, MVER);
  68         byte [] computed = hash(MNAME, MVER, "SHA-256");
  69         if (!MessageDigest.isEqual(expected, computed))
  70             throw new IOException("Expected and computed file hashes don't match");
  71     }
  72 
  73     void test() throws Exception {
  74         testEmptyModule();
  75     }
  76 
  77     /**
  78      * Get a module file's stored hash.
  79      */
  80     byte [] readHash(String name, String version) throws Exception {
  81         String fname = moduleDir + File.separator + name + "@" + version + ".jmod";
  82         try (FileInputStream fis = new FileInputStream(fname);
  83              DataInputStream in = new DataInputStream(fis);
  84              ModuleFile.Reader r = new ModuleFile.Reader(in);) {
  85              return r.getHash();
  86         }
  87     }
  88 
  89     /**
  90      * Hash a module file (without the file hash in the module file header).
  91      */
  92     static final int LENGTH_WITHOUT_HASH = 30; // computed from module-file format
  93     byte [] hash(String name, String version, String digest) throws Exception {
  94         String fname = moduleDir + File.separator + name + "@" + version + ".jmod";
  95         MessageDigest md = MessageDigest.getInstance(digest);
  96         try (FileInputStream fis = new FileInputStream(fname);
  97              DigestInputStream dis = new DigestInputStream(fis, md)) {
  98             dis.read(new byte[LENGTH_WITHOUT_HASH]);
  99             dis.on(false);
 100             dis.read(new byte [md.getDigestLength()]);
 101             dis.on(true);
 102             for (int c = dis.read() ; c != -1 ; c = dis.read())
 103                 ;
 104             return md.digest();
 105         }
 106     }
 107 
 108     /**
 109      * Compress a module.
 110      */
 111     void compress(String name) throws Exception {
 112         compress(name, false);
 113     }
 114 
 115     void compress(String name, boolean haveNatLibs)
 116         throws Exception {
 117         compress(name, haveNatLibs, false);
 118     }
 119 
 120     void compress(String name, boolean haveNatLibs,
 121                   boolean haveNatCmds) throws Exception {
 122         compress(name, haveNatLibs, haveNatCmds, false);
 123     }
 124 
 125     void compress(String name, boolean haveNatLibs,
 126                   boolean haveNatCmds, boolean haveConfig)
 127         throws Exception {
 128         List<String> args = new ArrayList<String>();
 129         args.add("-m");
 130         args.add(classesDir.getAbsolutePath());
 131         args.add("-d");
 132         args.add(moduleDir.getAbsolutePath());
 133         if (haveNatLibs) {
 134             args.add("--natlib");
 135             args.add(natlibDir.toString());
 136         }
 137         if (haveNatCmds) {
 138             args.add("--natcmd");
 139             args.add(natcmdDir.toString());
 140         }
 141         if (haveConfig) {
 142             args.add("--config");
 143             args.add(configDir.toString());
 144         }
 145         args.add("jmod");
 146         args.add("hello");
 147         Packager.main(args.toArray(new String[0]));
 148     }
 149 
 150     /**
 151      * Compile a list of files.
 152      */
 153     void compile(List<File> files) {
 154         List<String> options = new ArrayList<String>();
 155         options.addAll(Arrays.asList("-source", "8", "-d", classesDir.getPath()));
 156         for (File f: files)
 157             options.add(f.getPath());
 158 
 159         String[] opts = options.toArray(new String[options.size()]);
 160         StringWriter sw = new StringWriter();
 161         try (PrintWriter pw = new PrintWriter(sw)) {
 162             int rc = com.sun.tools.javac.Main.compile(opts, pw);
 163 
 164             String out = sw.toString();
 165             if (out.trim().length() > 0)
 166                 System.err.println(out);
 167             if (rc != 0)
 168                 throw new Error("compilation failed: rc=" + rc);
 169         }
 170     }
 171 
 172     /**
 173      * Add a file to a list if the file is not null.
 174      */
 175     void addFile(List<File> files, File file) {
 176         if (file != null)
 177             files.add(file);
 178     }
 179 
 180 
 181     /**
 182      * Create a test file with given content if the content is not null.
 183      */
 184     File createFile(String path, String body) throws IOException {
 185         if (body == null)
 186             return null;
 187         File file = new File(srcDir, path);
 188         file.getAbsoluteFile().getParentFile().mkdirs();
 189         try (FileWriter out = new FileWriter(file)) {
 190             out.write(body);
 191         }
 192         return file;
 193     }
 194 
 195     /**
 196      * Set up empty src and classes directories for a test.
 197      */
 198     void reset() {
 199         resetDir(srcDir);
 200         resetDir(classesDir);
 201         resetDir(moduleDir);
 202         resetDir(new File(MNAME));
 203     }
 204 
 205     /**
 206      * Set up an empty directory.
 207      */
 208     void resetDir(File dir) {
 209         if (dir.exists())
 210             deleteAll(dir);
 211         dir.mkdirs();
 212     }
 213 
 214     /**
 215      * Delete a file or a directory (including all its contents).
 216      */
 217     boolean deleteAll(File file) {
 218         if (file.isDirectory()) {
 219             for (File f: file.listFiles())
 220                 deleteAll(f);
 221         }
 222         return file.delete();
 223     }
 224 
 225     /**
 226      * Report an error.
 227      */
 228     void error(String msg, String... more) {
 229         System.err.println("error: " + msg);
 230         for (String s: more)
 231             System.err.println(s);
 232         errors++;
 233     }
 234 
 235     int count;
 236     int errors;
 237     File srcDir = new File("tmp", "src"); // use "tmp" to help avoid accidents
 238     File classesDir = new File("tmp", "classes");
 239     File moduleDir = new File("tmp", "modules");
 240     File natlibDir = new File(srcDir, "natlib");
 241     File natcmdDir = new File(srcDir, "natcmd");
 242     File configDir = new File(srcDir, "config");
 243 }