src/share/classes/org/openjdk/jigsaw/SimpleLibrary.java

Print this page




  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package org.openjdk.jigsaw;
  27 
  28 import java.lang.module.*;
  29 import java.io.*;
  30 import java.net.URI;

  31 import java.security.*;
  32 import java.security.cert.*;
  33 import java.util.*;
  34 import java.util.jar.*;
  35 import java.util.zip.*;
  36 
  37 import static java.nio.file.StandardCopyOption.*;
  38 
  39 /**
  40  * A simple module library which stores data directly in the filesystem
  41  *
  42  * @see Library
  43  */
  44 
  45 // ## TODO: Move remaining parent-searching logic upward into Library class
  46 
  47 // On-disk library layout
  48 //
  49 //   $LIB/%jigsaw-library
  50 //        com.foo.bar/1.2.3/info (= module-info.class)


  53 //                          classes/com/foo/bar/...
  54 //                          resources/com/foo/bar/...
  55 //                          lib/libbar.so
  56 //                          bin/bar
  57 //                          signer (signer's certchain & timestamp)
  58 //
  59 // ## Issue: Concurrent access to the module library
  60 // ## e.g. a module is being removed while a running application
  61 // ## is depending on it
  62 
  63 public final class SimpleLibrary
  64     extends Library
  65 {
  66 
  67     private static abstract class MetaData {
  68 
  69         protected final int maxMajorVersion;
  70         protected final int maxMinorVersion;
  71         protected int majorVersion;
  72         protected int minorVersion;
  73         private FileConstants.Type type;
  74         private File file;
  75 
  76         protected MetaData(int maxMajor, int maxMinor,
  77                            FileConstants.Type t, File f)
  78         {
  79             maxMajorVersion = majorVersion = maxMajor;
  80             maxMinorVersion = minorVersion = maxMinor;
  81             type = t;
  82             file = f;
  83         }
  84 
  85         protected abstract void storeRest(DataOutputStream out)
  86             throws IOException;
  87 
  88         void store() throws IOException {
  89             OutputStream fo = new FileOutputStream(file);
  90             DataOutputStream out
  91                 = new DataOutputStream(new BufferedOutputStream(fo));
  92             try {
  93                 out.writeInt(FileConstants.MAGIC);
  94                 out.writeShort(type.value());
  95                 out.writeShort(majorVersion);
  96                 out.writeShort(minorVersion);
  97                 storeRest(out);
  98             } finally {
  99                 out.close();
 100             }
 101         }
 102 
 103         protected abstract void loadRest(DataInputStream in)
 104             throws IOException;
 105 
 106         protected void load() throws IOException {
 107             InputStream fi = new FileInputStream(file);
 108             try {
 109                 DataInputStream in
 110                     = new DataInputStream(new BufferedInputStream(fi));
 111                 int m = in.readInt();
 112                 if (m != FileConstants.MAGIC)
 113                     throw new IOException(file + ": Invalid magic number");
 114                 int typ = in.readShort();
 115                 if (typ != type.value())
 116                     throw new IOException(file + ": Invalid file type");
 117                 int maj = in.readShort();
 118                 int min = in.readShort();
 119                 if (   maj > maxMajorVersion
 120                     || (maj == maxMajorVersion && min > maxMinorVersion)) {
 121                     throw new IOException(file
 122                                           + ": Futuristic version number");
 123                 }
 124                 majorVersion = maj;
 125                 minorVersion = min;
 126                 loadRest(in);
 127             } catch (EOFException x) {
 128                 throw new IOException(file + ": Invalid library metadata",
 129                                       x);
 130             } finally {
 131                 fi.close();
 132             }
 133         }
 134 
 135     }
 136 
 137     /**
 138      * Defines the storage options that SimpleLibrary supports.
 139      */
 140     public static enum StorageOption {
 141         DEFLATED,
 142     }
 143 
 144     private static final class Header
 145         extends MetaData
 146     {
 147         private static final String FILE
 148             = FileConstants.META_PREFIX + "jigsaw-library";
 149 
 150         private static final int MAJOR_VERSION = 0;
 151         private static final int MINOR_VERSION = 1;
 152 
 153         private static final int DEFLATED = 1 << 0;
 154 
 155         private File parent;









 156         private Set<StorageOption> opts;
 157 
 158         public File parent() { return parent; }



 159         public boolean isDeflated() {
 160            return opts.contains(StorageOption.DEFLATED);
 161         }
 162 
 163         private Header(File root, File p, Set<StorageOption> opts) {
 164             super(MAJOR_VERSION, MINOR_VERSION,
 165                   FileConstants.Type.LIBRARY_HEADER,
 166                   new File(root, FILE));
 167             this.parent = p;








 168             this.opts = new HashSet<>(opts);
 169         }
 170 
 171         private Header(File root) {
 172             this(root, null, Collections.<StorageOption>emptySet());




 173         }
 174 
 175         protected void storeRest(DataOutputStream out)
 176             throws IOException
 177         {
 178             int flags = 0;
 179             if (isDeflated())
 180                 flags |= DEFLATED;
 181             out.writeShort(flags);
 182             out.writeByte((parent != null) ? 1 : 0);
 183             if (parent != null)
 184                 out.writeUTF(parent.toString());


 185         }
 186 
 187         protected void loadRest(DataInputStream in)
 188             throws IOException
 189         {




 190             opts = new HashSet<StorageOption>();
 191             int flags = in.readShort();
 192             if ((flags & DEFLATED) == DEFLATED)
 193                 opts.add(StorageOption.DEFLATED);
 194             int b = in.readByte();
 195             if (b != 0)
 196                 parent = new File(in.readUTF());

 197         }
 198 
 199         private static Header load(File f)
 200             throws IOException
 201         {
 202             Header h = new Header(f);
 203             h.load();
 204             return h;
 205         }
 206 
 207     }
 208 
 209     private final File root;
 210     private final File canonicalRoot;
 211     private File parentPath = null;
 212     private SimpleLibrary parent = null;



 213     private final Header hd;
 214 
 215     public String name() { return root.toString(); }
 216     public File root() { return canonicalRoot; }
 217     public int majorVersion() { return hd.majorVersion; }
 218     public int minorVersion() { return hd.minorVersion; }
 219     public SimpleLibrary parent() { return parent; }



 220     public boolean isDeflated() { return hd.isDeflated(); }
 221 
 222     private URI location = null;
 223     public URI location() {
 224         if (location == null)
 225             location = root().toURI();
 226         return location;
 227     }
 228 
 229     @Override
 230     public String toString() {
 231         return (this.getClass().getName()
 232                 + "[" + canonicalRoot
 233                 + ", v" + hd.majorVersion + "." + hd.minorVersion + "]");
 234     }
 235 
 236     private SimpleLibrary(File path, boolean create, File parentPath, Set<StorageOption> opts)
 237         throws IOException
 238     {



















 239         root = path;
 240         canonicalRoot = root.getCanonicalFile();
 241         if (root.exists()) {
 242             if (!root.isDirectory())
 243                 throw new IOException(root + ": Exists but is not a directory");
 244             hd = Header.load(root);
 245             if (hd.parent() != null) {
 246                 parent = open(hd.parent());
 247                 parentPath = hd.parent();
 248             }
 249             return;






 250         }
 251         if (!create)
 252             throw new FileNotFoundException(root.toString());














 253         if (parentPath != null) {
 254             this.parent = open(parentPath);
 255             this.parentPath = this.parent.root();
 256         }
 257         if (!root.mkdirs())
 258             throw new IOException(root + ": Cannot create library directory");
 259         hd = new Header(canonicalRoot, this.parentPath, opts);




 260         hd.store();
 261     }
 262 








 263     public static SimpleLibrary create(File path, File parent, Set<StorageOption> opts)
 264         throws IOException
 265     {
 266         return new SimpleLibrary(path, true, parent, opts);
 267     }
 268 
 269     public static SimpleLibrary create(File path, File parent)
 270         throws IOException 
 271     {
 272         return new SimpleLibrary(path, true, parent, Collections.<StorageOption>emptySet());
 273     }
 274 
 275     public static SimpleLibrary create(File path, Set<StorageOption> opts)
 276         throws IOException
 277     {
 278         // ## Should default parent to $JAVA_HOME/lib/modules
 279         return new SimpleLibrary(path, true, null, opts);
 280     }
 281 
 282     public static SimpleLibrary open(File path)
 283         throws IOException
 284     {
 285         return new SimpleLibrary(path, false, null, Collections.<StorageOption>emptySet());
 286     }
 287 
 288     private static final JigsawModuleSystem jms
 289         = JigsawModuleSystem.instance();
 290 
 291     private static final class Index
 292         extends MetaData
 293     {
 294 
 295         private static String FILE = "index";
 296 
 297         private static int MAJOR_VERSION = 0;
 298         private static int MINOR_VERSION = 1;
 299 
 300         private Set<String> publicClasses;
 301         public Set<String> publicClasses() { return publicClasses; }
 302 
 303         private Set<String> otherClasses;
 304         public Set<String> otherClasses() { return otherClasses; }
 305 


 981 
 982             if (verifySignature && mr.hasSignature()) {
 983                 ModuleFileVerifier mfv = new SignedModule.PKCS7Verifier(mr);
 984                 if (mfvParams == null) {
 985                     mfvParams = new SignedModule.VerifierParameters();
 986                 }
 987                 // Verify the module signature and validate the signer's
 988                 // certificate chain
 989                 Set<CodeSigner> signers = mfv.verifySignature(mfvParams);
 990 
 991                 // Verify the module header hash and the module info hash
 992                 mfv.verifyHashesStart(mfvParams);
 993 
 994                 // ## Check policy - is signer trusted and what permissions
 995                 // ## should be granted?
 996 
 997                 // Store signer info
 998                 new Signers(md, signers).store();
 999 
1000                 // Read and verify the rest of the hashes
1001                 mr.readRest(md, isDeflated());
1002                 mfv.verifyHashesRest(mfvParams);
1003             } else {
1004                 mr.readRest(md, isDeflated());
1005             }
1006  
1007             if (strip) 
1008                 strip(md);
1009             reIndex(mid);         // ## Could do this while reading module file
1010             return mid;
1011 
1012         } catch (IOException | SignatureException x) {
1013             if (md != null && md.exists()) {
1014                 try {
1015                     Files.deleteTree(md);
1016                 } catch (IOException y) {
1017                     y.initCause(x);
1018                     throw y;
1019                 }
1020             }
1021             throw x;
1022         }
1023     }
1024 
1025     private ModuleId installFromJarFile(File mf, boolean verifySignature, boolean strip)
1026         throws ConfigurationException, IOException, SignatureException
1027     {
1028         File md = null;
1029         try (JarFile jf = new JarFile(mf, verifySignature)) {
1030             ModuleInfo mi = jf.getModuleInfo();
1031             if (mi == null)
1032                 throw new ConfigurationException(mf + ": not a modular JAR file");
1033 
1034             md = moduleDir(mi.id());
1035             ModuleId mid = mi.id();


1169                 entry.setSize(size);
1170                 entry.setCrc(je.getCrc());
1171                 entry.setCompressedSize(size);
1172             }
1173             jos.putNextEntry(entry);
1174             if (baos.size() > 0)
1175                 baos.writeTo(jos);
1176             jos.closeEntry();
1177         } catch (SecurityException se) {
1178             throw new SignatureException(se);
1179         }
1180     }
1181 
1182     private ModuleId install(File mf, boolean verifySignature, boolean strip)
1183         throws ConfigurationException, IOException, SignatureException
1184     {
1185         ModuleId mid;
1186         if (mf.getName().endsWith(".jar"))
1187             mid = installFromJarFile(mf, verifySignature, strip);
1188         else {

1189             try (FileInputStream in = new FileInputStream(mf)) {
1190                 mid = install(in, verifySignature, strip);
1191             }
1192         }
1193         return mid;
1194     }
1195 
1196     public void install(Collection<File> mfs, boolean verifySignature, boolean strip)
1197         throws ConfigurationException, IOException, SignatureException
1198     {
1199         List<ModuleId> mids = new ArrayList<>();
1200         boolean complete = false;
1201         Throwable ox = null;
1202         try {
1203             for (File mf : mfs)
1204                 mids.add(install(mf, verifySignature, strip));
1205             configure(mids);
1206             complete = true;
1207         } catch (IOException|ConfigurationException x) {
1208             ox = x;


1321         throws ConfigurationException, IOException
1322     {
1323         // ## mids not used yet
1324         for (ModuleInfo mi : listLocalRootModuleInfos()) {
1325             // ## We could be a lot more clever about this!
1326             Configuration<Context> cf
1327                 = Configurator.configure(this, mi.id().toQuery());
1328             new StoredConfiguration(moduleDir(mi.id()), cf).store();
1329         }
1330     }
1331 
1332     public URI findLocalResource(ModuleId mid, String name)
1333         throws IOException
1334     {
1335         return locateContent(mid, name);
1336     }
1337 
1338     public File findLocalNativeLibrary(ModuleId mid, String name)
1339         throws IOException
1340     {
1341         File md = findModuleDir(mid);
1342         if (md == null)


1343             return null;
1344         File f = new File(new File(md, "lib"), name);


1345         if (!f.exists())
1346             return null;
1347         return f;
1348     }
1349 
1350     public File classPath(ModuleId mid)
1351         throws IOException
1352     {
1353         File md = findModuleDir(mid);
1354         if (md == null) {
1355             if (parent != null)
1356                 return parent.classPath(mid);
1357             return null;
1358         }
1359         // ## Check for other formats here
1360         return new File(md, "classes");
1361     }
1362 
1363     /**
1364      * <p> Re-index the classes of the named previously-installed modules, and




  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package org.openjdk.jigsaw;
  27 
  28 import java.lang.module.*;
  29 import java.io.*;
  30 import java.net.URI;
  31 import java.nio.file.*;
  32 import java.security.*;
  33 import java.security.cert.*;
  34 import java.util.*;
  35 import java.util.jar.*;
  36 import java.util.zip.*;
  37 
  38 import static java.nio.file.StandardCopyOption.*;
  39 
  40 /**
  41  * A simple module library which stores data directly in the filesystem
  42  *
  43  * @see Library
  44  */
  45 
  46 // ## TODO: Move remaining parent-searching logic upward into Library class
  47 
  48 // On-disk library layout
  49 //
  50 //   $LIB/%jigsaw-library
  51 //        com.foo.bar/1.2.3/info (= module-info.class)


  54 //                          classes/com/foo/bar/...
  55 //                          resources/com/foo/bar/...
  56 //                          lib/libbar.so
  57 //                          bin/bar
  58 //                          signer (signer's certchain & timestamp)
  59 //
  60 // ## Issue: Concurrent access to the module library
  61 // ## e.g. a module is being removed while a running application
  62 // ## is depending on it
  63 
  64 public final class SimpleLibrary
  65     extends Library
  66 {
  67 
  68     private static abstract class MetaData {
  69 
  70         protected final int maxMajorVersion;
  71         protected final int maxMinorVersion;
  72         protected int majorVersion;
  73         protected int minorVersion;
  74         private final FileConstants.Type type;
  75         private final File file;
  76 
  77         protected MetaData(int maxMajor, int maxMinor,
  78                            FileConstants.Type t, File f)
  79         {
  80             maxMajorVersion = majorVersion = maxMajor;
  81             maxMinorVersion = minorVersion = maxMinor;
  82             type = t;
  83             file = f;
  84         }
  85 
  86         protected abstract void storeRest(DataOutputStream out)
  87             throws IOException;
  88 
  89         void store() throws IOException {
  90             try (OutputStream fos = new FileOutputStream(file);
  91                  BufferedOutputStream bos = new BufferedOutputStream(fos);
  92                  DataOutputStream out = new DataOutputStream(bos)) {

  93                 out.writeInt(FileConstants.MAGIC);
  94                 out.writeShort(type.value());
  95                 out.writeShort(majorVersion);
  96                 out.writeShort(minorVersion);
  97                 storeRest(out);


  98             }
  99         }
 100 
 101         protected abstract void loadRest(DataInputStream in)
 102             throws IOException;
 103 
 104         protected void load() throws IOException {
 105             try (InputStream fis = new FileInputStream(file);
 106                  BufferedInputStream bis = new BufferedInputStream(fis);
 107                  DataInputStream in = new DataInputStream(fis)) {
 108                 if (in.readInt() != FileConstants.MAGIC)


 109                     throw new IOException(file + ": Invalid magic number");
 110                 if (in.readShort() != type.value())

 111                     throw new IOException(file + ": Invalid file type");
 112                 int maj = in.readShort();
 113                 int min = in.readShort();
 114                 if (   maj > maxMajorVersion
 115                     || (maj == maxMajorVersion && min > maxMinorVersion)) {
 116                     throw new IOException(file
 117                                           + ": Futuristic version number");
 118                 }
 119                 majorVersion = maj;
 120                 minorVersion = min;
 121                 loadRest(in);
 122             } catch (EOFException x) {
 123                 throw new IOException(file + ": Invalid library metadata", x);



 124             }
 125         }

 126     }
 127 
 128     /**
 129      * Defines the storage options that SimpleLibrary supports.
 130      */
 131     public static enum StorageOption {
 132         DEFLATED,
 133     }
 134 
 135     private static final class Header
 136         extends MetaData
 137     {
 138         private static final String FILE
 139             = FileConstants.META_PREFIX + "jigsaw-library";
 140 
 141         private static final int MAJOR_VERSION = 0;
 142         private static final int MINOR_VERSION = 1;
 143 
 144         private static final int DEFLATED = 1 << 0;
 145 
 146         private File parent;
 147         // location of native libs for this library (may be outside the library)
 148         // null:default, to use a per-module 'lib' directory
 149         private File natlibs;
 150         // location of native cmds for this library (may be outside the library)
 151         // null:default, to use a per-module 'bin' directory
 152         private File natcmds;
 153         // location of config files for this library (may be outside the library)
 154         // null:default, to use a per-module 'etc' directory
 155         private File configs;
 156         private Set<StorageOption> opts;
 157 
 158         public File parent()  { return parent;  }
 159         public File natlibs() { return natlibs; }
 160         public File natcmds() { return natcmds; }
 161         public File configs() { return configs; }
 162         public boolean isDeflated() {
 163             return opts.contains(StorageOption.DEFLATED);
 164         }
 165 
 166         private Header(File root) {
 167              super(MAJOR_VERSION, MINOR_VERSION,
 168                   FileConstants.Type.LIBRARY_HEADER,
 169                   new File(root, FILE));
 170         }
 171 
 172         private Header(File root, File parent, File natlibs, File natcmds,
 173                        File configs, Set<StorageOption> opts) {
 174             this(root);
 175             this.parent = parent;
 176             this.natlibs = natlibs;
 177             this.natcmds = natcmds;
 178             this.configs = configs;
 179             this.opts = new HashSet<>(opts);
 180         }
 181 
 182         private void storePath(File p, DataOutputStream out) throws IOException {  
 183             if (p != null) {
 184                 out.writeByte(1);
 185                 out.writeUTF(Files.convertSeparator(p.toString()));
 186             } else 
 187                 out.write(0);
 188         }
 189 
 190         protected void storeRest(DataOutputStream out) throws IOException {


 191             int flags = 0;
 192             if (isDeflated())
 193                 flags |= DEFLATED;
 194             out.writeShort(flags);
 195             
 196             storePath(parent, out);
 197             storePath(natlibs, out);
 198             storePath(natcmds, out);
 199             storePath(configs, out);
 200         }
 201         
 202         private File loadPath(DataInputStream in) throws IOException {  
 203             if (in.readByte() != 0)
 204                 return new File(Files.platformSeparator(in.readUTF()));
 205             return null;
 206         }
 207 
 208         protected void loadRest(DataInputStream in) throws IOException {
 209             opts = new HashSet<StorageOption>();
 210             int flags = in.readShort();
 211             if ((flags & DEFLATED) == DEFLATED)
 212                 opts.add(StorageOption.DEFLATED);
 213             parent = loadPath(in);
 214             natlibs = loadPath(in);
 215             natcmds = loadPath(in);
 216             configs = loadPath(in);
 217         }
 218 
 219         private static Header load(File f) throws IOException {


 220             Header h = new Header(f);
 221             h.load();
 222             return h;
 223         }

 224     }
 225 
 226     private final File root;
 227     private final File canonicalRoot;
 228     private File parentPath;
 229     private File natlibs;
 230     private File natcmds;
 231     private File configs;
 232     private SimpleLibrary parent;
 233     private final Header hd;
 234 
 235     public String name() { return root.toString(); }
 236     public File root() { return canonicalRoot; }
 237     public int majorVersion() { return hd.majorVersion; }
 238     public int minorVersion() { return hd.minorVersion; }
 239     public SimpleLibrary parent() { return parent; }
 240     public File natlibs() { return natlibs; }
 241     public File natcmds() { return natcmds; }
 242     public File configs() { return configs; }
 243     public boolean isDeflated() { return hd.isDeflated(); }
 244 
 245     private URI location = null;
 246     public URI location() {
 247         if (location == null)
 248             location = root().toURI();
 249         return location;
 250     }
 251 
 252     @Override
 253     public String toString() {
 254         return (this.getClass().getName()
 255                 + "[" + canonicalRoot
 256                 + ", v" + hd.majorVersion + "." + hd.minorVersion + "]");
 257     }
 258 
 259 
 260     private static File resolveAndEnsurePath(File path) throws IOException {
 261         if (path == null) { return null; }
 262         
 263         File p = path.getCanonicalFile();
 264         if (!p.exists())
 265             Files.mkdirs(p, p.toString());
 266         else {
 267             Files.ensureIsDirectory(p);
 268             Files.ensureWriteable(p);
 269         }
 270         return p;
 271     }
 272 
 273     private File relativize(File path) throws IOException {
 274         if (path == null) { return null; }
 275         // Return the path relative to the canonical root
 276         return (canonicalRoot.toPath().relativize(path.toPath().toRealPath())).toFile();
 277     }
 278 
 279     // Opens an existing library
 280     private SimpleLibrary(File path) throws IOException {
 281         root = path;
 282         canonicalRoot = root.getCanonicalFile();
 283         Files.ensureIsDirectory(root);


 284         hd = Header.load(root);
 285         if (hd.parent() != null) {
 286             parent = open(hd.parent());
 287             parentPath = hd.parent();
 288         }
 289 
 290         if (hd.natlibs() != null)
 291             natlibs = new File(canonicalRoot, hd.natlibs().toString()).getCanonicalFile();
 292         if (hd.natcmds() != null)
 293             natcmds = new File(canonicalRoot, hd.natcmds().toString()).getCanonicalFile();
 294         if (hd.configs() != null)
 295             configs = new File(canonicalRoot, hd.configs().toString()).getCanonicalFile();
 296     }
 297 
 298     // Creates a new library
 299     private SimpleLibrary(File path, File parentPath, File natlibs, File natcmds,
 300                           File configs, Set<StorageOption> opts)
 301         throws IOException
 302     {
 303         root = path;
 304         canonicalRoot = root.getCanonicalFile();
 305         if (root.exists()) {
 306             Files.ensureIsDirectory(root);
 307             if (root.list().length != 0)
 308                 throw new IOException(root + ": Already Exists");
 309             Files.ensureWriteable(root);
 310         } else
 311             Files.mkdirs(root, root.toString());
 312 
 313         if (parentPath != null) {
 314             this.parent = open(parentPath);
 315             this.parentPath = this.parent.root();
 316         }
 317 
 318         this.natlibs = resolveAndEnsurePath(natlibs);
 319         this.natcmds = resolveAndEnsurePath(natcmds);
 320         this.configs = resolveAndEnsurePath(configs);
 321 
 322         hd = new Header(canonicalRoot, this.parentPath, relativize(this.natlibs),
 323                         relativize(this.natcmds), relativize(this.configs), opts);
 324         hd.store();
 325     }
 326 
 327     public static SimpleLibrary create(File path, File parent, File natlibs,
 328                                        File natcmds, File configs,
 329                                        Set<StorageOption> opts)
 330         throws IOException
 331     {
 332         return new SimpleLibrary(path, parent, natlibs, natcmds, configs, opts);
 333     }
 334 
 335     public static SimpleLibrary create(File path, File parent, Set<StorageOption> opts)
 336         throws IOException
 337     {
 338         return new SimpleLibrary(path, parent, null, null, null, opts);
 339     }
 340 
 341     public static SimpleLibrary create(File path, File parent)
 342         throws IOException
 343     {
 344         return SimpleLibrary.create(path, parent, Collections.<StorageOption>emptySet());
 345     }
 346 
 347     public static SimpleLibrary create(File path, Set<StorageOption> opts)
 348         throws IOException
 349     {
 350         // ## Should default parent to $JAVA_HOME/lib/modules
 351         return SimpleLibrary.create(path, null, opts);
 352     }
 353 
 354     public static SimpleLibrary open(File path)
 355         throws IOException
 356     {
 357         return new SimpleLibrary(path);
 358     }
 359 
 360     private static final JigsawModuleSystem jms
 361         = JigsawModuleSystem.instance();
 362 
 363     private static final class Index
 364         extends MetaData
 365     {
 366 
 367         private static String FILE = "index";
 368 
 369         private static int MAJOR_VERSION = 0;
 370         private static int MINOR_VERSION = 1;
 371 
 372         private Set<String> publicClasses;
 373         public Set<String> publicClasses() { return publicClasses; }
 374 
 375         private Set<String> otherClasses;
 376         public Set<String> otherClasses() { return otherClasses; }
 377 


1053 
1054             if (verifySignature && mr.hasSignature()) {
1055                 ModuleFileVerifier mfv = new SignedModule.PKCS7Verifier(mr);
1056                 if (mfvParams == null) {
1057                     mfvParams = new SignedModule.VerifierParameters();
1058                 }
1059                 // Verify the module signature and validate the signer's
1060                 // certificate chain
1061                 Set<CodeSigner> signers = mfv.verifySignature(mfvParams);
1062 
1063                 // Verify the module header hash and the module info hash
1064                 mfv.verifyHashesStart(mfvParams);
1065 
1066                 // ## Check policy - is signer trusted and what permissions
1067                 // ## should be granted?
1068 
1069                 // Store signer info
1070                 new Signers(md, signers).store();
1071 
1072                 // Read and verify the rest of the hashes
1073                 mr.readRest(md, isDeflated(), natlibs(), natcmds(), configs());
1074                 mfv.verifyHashesRest(mfvParams);
1075             } else {
1076                 mr.readRest(md, isDeflated(), natlibs(), natcmds(), configs());
1077             }
1078 
1079             if (strip)
1080                 strip(md);
1081             reIndex(mid);         // ## Could do this while reading module file
1082             return mid;
1083 
1084         } catch (IOException | SignatureException x) {
1085             if (md != null && md.exists()) {
1086                 try {
1087                    ModuleFile.Reader.remove(md);
1088                 } catch (IOException y) {
1089                     y.initCause(x);
1090                     throw y;
1091                 }
1092             }
1093             throw x;
1094         }
1095     }
1096 
1097     private ModuleId installFromJarFile(File mf, boolean verifySignature, boolean strip)
1098         throws ConfigurationException, IOException, SignatureException
1099     {
1100         File md = null;
1101         try (JarFile jf = new JarFile(mf, verifySignature)) {
1102             ModuleInfo mi = jf.getModuleInfo();
1103             if (mi == null)
1104                 throw new ConfigurationException(mf + ": not a modular JAR file");
1105 
1106             md = moduleDir(mi.id());
1107             ModuleId mid = mi.id();


1241                 entry.setSize(size);
1242                 entry.setCrc(je.getCrc());
1243                 entry.setCompressedSize(size);
1244             }
1245             jos.putNextEntry(entry);
1246             if (baos.size() > 0)
1247                 baos.writeTo(jos);
1248             jos.closeEntry();
1249         } catch (SecurityException se) {
1250             throw new SignatureException(se);
1251         }
1252     }
1253 
1254     private ModuleId install(File mf, boolean verifySignature, boolean strip)
1255         throws ConfigurationException, IOException, SignatureException
1256     {
1257         ModuleId mid;
1258         if (mf.getName().endsWith(".jar"))
1259             mid = installFromJarFile(mf, verifySignature, strip);
1260         else {
1261             // Assume jmod file
1262             try (FileInputStream in = new FileInputStream(mf)) {
1263                 mid = install(in, verifySignature, strip);
1264             }
1265         }
1266         return mid;
1267     }
1268 
1269     public void install(Collection<File> mfs, boolean verifySignature, boolean strip)
1270         throws ConfigurationException, IOException, SignatureException
1271     {
1272         List<ModuleId> mids = new ArrayList<>();
1273         boolean complete = false;
1274         Throwable ox = null;
1275         try {
1276             for (File mf : mfs)
1277                 mids.add(install(mf, verifySignature, strip));
1278             configure(mids);
1279             complete = true;
1280         } catch (IOException|ConfigurationException x) {
1281             ox = x;


1394         throws ConfigurationException, IOException
1395     {
1396         // ## mids not used yet
1397         for (ModuleInfo mi : listLocalRootModuleInfos()) {
1398             // ## We could be a lot more clever about this!
1399             Configuration<Context> cf
1400                 = Configurator.configure(this, mi.id().toQuery());
1401             new StoredConfiguration(moduleDir(mi.id()), cf).store();
1402         }
1403     }
1404 
1405     public URI findLocalResource(ModuleId mid, String name)
1406         throws IOException
1407     {
1408         return locateContent(mid, name);
1409     }
1410 
1411     public File findLocalNativeLibrary(ModuleId mid, String name)
1412         throws IOException
1413     {
1414         File f = natlibs();
1415         if (f == null) {
1416             f = findModuleDir(mid);
1417             if (f == null)
1418                 return null;
1419             f = new File(f, "lib");
1420         }
1421         f = new File(f, name);
1422         if (!f.exists())
1423             return null;
1424         return f;
1425     }
1426 
1427     public File classPath(ModuleId mid)
1428         throws IOException
1429     {
1430         File md = findModuleDir(mid);
1431         if (md == null) {
1432             if (parent != null)
1433                 return parent.classPath(mid);
1434             return null;
1435         }
1436         // ## Check for other formats here
1437         return new File(md, "classes");
1438     }
1439 
1440     /**
1441      * <p> Re-index the classes of the named previously-installed modules, and