JrtFileSystemProvider.java revision 13901:b2a69d66dc65
1/* 2 * Copyright (c) 2014, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 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 */ 25package jdk.internal.jrtfs; 26 27import java.io.*; 28import java.net.MalformedURLException; 29import java.net.URL; 30import java.net.URLClassLoader; 31import java.nio.channels.*; 32import java.nio.file.*; 33import java.nio.file.DirectoryStream.Filter; 34import java.nio.file.attribute.*; 35import java.nio.file.spi.FileSystemProvider; 36import java.net.URI; 37import java.security.AccessController; 38import java.security.PrivilegedAction; 39import java.util.HashMap; 40import java.util.Map; 41import java.util.Objects; 42import java.util.Set; 43import java.util.concurrent.ExecutorService; 44 45/** 46 * File system provider for jrt file systems. Conditionally creates jrt fs on 47 * .jimage file or exploded modules directory of underlying JDK. 48 * 49 * @implNote This class needs to maintain JDK 8 source compatibility. 50 * 51 * It is used internally in the JDK to implement jimage/jrtfs access, 52 * but also compiled and delivered as part of the jrtfs.jar to support access 53 * to the jimage file provided by the shipped JDK by tools running on JDK 8. 54 */ 55public final class JrtFileSystemProvider extends FileSystemProvider { 56 57 private volatile FileSystem theFileSystem; 58 59 public JrtFileSystemProvider() { 60 } 61 62 @Override 63 public String getScheme() { 64 return "jrt"; 65 } 66 67 /** 68 * Need FilePermission ${java.home}/-", "read" to create or get jrt:/ 69 */ 70 private void checkPermission() { 71 SecurityManager sm = System.getSecurityManager(); 72 if (sm != null) { 73 String home = SystemImages.RUNTIME_HOME; 74 FilePermission perm 75 = new FilePermission(home + File.separator + "-", "read"); 76 sm.checkPermission(perm); 77 } 78 } 79 80 private void checkUri(URI uri) { 81 if (!uri.getScheme().equalsIgnoreCase(getScheme())) { 82 throw new IllegalArgumentException("URI does not match this provider"); 83 } 84 if (uri.getAuthority() != null) { 85 throw new IllegalArgumentException("Authority component present"); 86 } 87 if (uri.getPath() == null) { 88 throw new IllegalArgumentException("Path component is undefined"); 89 } 90 if (!uri.getPath().equals("/")) { 91 throw new IllegalArgumentException("Path component should be '/'"); 92 } 93 if (uri.getQuery() != null) { 94 throw new IllegalArgumentException("Query component present"); 95 } 96 if (uri.getFragment() != null) { 97 throw new IllegalArgumentException("Fragment component present"); 98 } 99 } 100 101 @Override 102 public FileSystem newFileSystem(URI uri, Map<String, ?> env) 103 throws IOException { 104 checkPermission(); 105 checkUri(uri); 106 107 if (env != null && env.containsKey("java.home")) { 108 return newFileSystem((String)env.get("java.home"), uri, env); 109 } else { 110 return SystemImages.hasModulesImage() 111 ? new JrtFileSystem(this, env) 112 : new JrtExplodedFileSystem(this, env); 113 } 114 } 115 116 private static final String JRT_FS_JAR = "jrt-fs.jar"; 117 private FileSystem newFileSystem(String targetHome, URI uri, Map<String, ?> env) 118 throws IOException { 119 Objects.requireNonNull(targetHome); 120 Path jrtfs = FileSystems.getDefault().getPath(targetHome, JRT_FS_JAR); 121 if (Files.notExists(jrtfs)) { 122 throw new IOException(jrtfs.toString() + " not exist"); 123 } 124 125 Map<String,?> newEnv = new HashMap<>(env); 126 newEnv.remove("java.home"); 127 ClassLoader cl = newJrtFsLoader(jrtfs); 128 try { 129 Class<?> c = Class.forName(JrtFileSystemProvider.class.getName(), false, cl); 130 return ((FileSystemProvider)c.newInstance()).newFileSystem(uri, newEnv); 131 } catch (ClassNotFoundException | 132 IllegalAccessException | 133 InstantiationException e) { 134 throw new IOException(e); 135 } 136 } 137 138 private static class JrtFsLoader extends URLClassLoader { 139 JrtFsLoader(URL[] urls) { 140 super(urls); 141 } 142 143 @Override 144 protected Class<?> loadClass(String cn, boolean resolve) 145 throws ClassNotFoundException 146 { 147 Class<?> c = findLoadedClass(cn); 148 if (c == null) { 149 URL u = findResource(cn.replace('.', '/') + ".class"); 150 if (u != null) { 151 c = findClass(cn); 152 } else { 153 return super.loadClass(cn, resolve); 154 } 155 } 156 if (resolve) 157 resolveClass(c); 158 return c; 159 } 160 } 161 162 private static URLClassLoader newJrtFsLoader(Path jrtfs) { 163 final URL url; 164 try { 165 url = jrtfs.toUri().toURL(); 166 } catch (MalformedURLException mue) { 167 throw new IllegalArgumentException(mue); 168 } 169 170 final URL[] urls = new URL[] { url }; 171 return AccessController.doPrivileged( 172 new PrivilegedAction<URLClassLoader>() { 173 @Override 174 public URLClassLoader run() { 175 return new JrtFsLoader(urls); 176 } 177 } 178 ); 179 } 180 181 @Override 182 public Path getPath(URI uri) { 183 checkPermission(); 184 if (!uri.getScheme().equalsIgnoreCase(getScheme())) { 185 throw new IllegalArgumentException("URI does not match this provider"); 186 } 187 if (uri.getAuthority() != null) { 188 throw new IllegalArgumentException("Authority component present"); 189 } 190 if (uri.getQuery() != null) { 191 throw new IllegalArgumentException("Query component present"); 192 } 193 if (uri.getFragment() != null) { 194 throw new IllegalArgumentException("Fragment component present"); 195 } 196 String path = uri.getPath(); 197 if (path == null || path.charAt(0) != '/') { 198 throw new IllegalArgumentException("Invalid path component"); 199 } 200 return getTheFileSystem().getPath(path); 201 } 202 203 private FileSystem getTheFileSystem() { 204 checkPermission(); 205 FileSystem fs = this.theFileSystem; 206 if (fs == null) { 207 synchronized (this) { 208 fs = this.theFileSystem; 209 if (fs == null) { 210 try { 211 if (SystemImages.hasModulesImage()) { 212 this.theFileSystem = fs = new JrtFileSystem(this, null) { 213 @Override 214 public void close() { 215 throw new UnsupportedOperationException(); 216 } 217 }; 218 } else { 219 this.theFileSystem = fs = new JrtExplodedFileSystem(this, null) { 220 @Override 221 public void close() { 222 throw new UnsupportedOperationException(); 223 } 224 }; 225 } 226 } catch (IOException ioe) { 227 throw new InternalError(ioe); 228 } 229 } 230 } 231 } 232 return fs; 233 } 234 235 @Override 236 public FileSystem getFileSystem(URI uri) { 237 checkPermission(); 238 checkUri(uri); 239 return getTheFileSystem(); 240 } 241 242 // Checks that the given file is a JrtPath 243 static final AbstractJrtPath toAbstractJrtPath(Path path) { 244 if (path == null) { 245 throw new NullPointerException(); 246 } 247 if (!(path instanceof AbstractJrtPath)) { 248 throw new ProviderMismatchException(); 249 } 250 return (AbstractJrtPath) path; 251 } 252 253 @Override 254 public void checkAccess(Path path, AccessMode... modes) throws IOException { 255 toAbstractJrtPath(path).checkAccess(modes); 256 } 257 258 @Override 259 public Path readSymbolicLink(Path link) throws IOException { 260 return toAbstractJrtPath(link).readSymbolicLink(); 261 } 262 263 @Override 264 public void copy(Path src, Path target, CopyOption... options) 265 throws IOException { 266 toAbstractJrtPath(src).copy(toAbstractJrtPath(target), options); 267 } 268 269 @Override 270 public void createDirectory(Path path, FileAttribute<?>... attrs) 271 throws IOException { 272 toAbstractJrtPath(path).createDirectory(attrs); 273 } 274 275 @Override 276 public final void delete(Path path) throws IOException { 277 toAbstractJrtPath(path).delete(); 278 } 279 280 @Override 281 @SuppressWarnings("unchecked") 282 public <V extends FileAttributeView> V 283 getFileAttributeView(Path path, Class<V> type, LinkOption... options) { 284 return JrtFileAttributeView.get(toAbstractJrtPath(path), type, options); 285 } 286 287 @Override 288 public FileStore getFileStore(Path path) throws IOException { 289 return toAbstractJrtPath(path).getFileStore(); 290 } 291 292 @Override 293 public boolean isHidden(Path path) { 294 return toAbstractJrtPath(path).isHidden(); 295 } 296 297 @Override 298 public boolean isSameFile(Path path, Path other) throws IOException { 299 return toAbstractJrtPath(path).isSameFile(other); 300 } 301 302 @Override 303 public void move(Path src, Path target, CopyOption... options) 304 throws IOException { 305 toAbstractJrtPath(src).move(toAbstractJrtPath(target), options); 306 } 307 308 @Override 309 public AsynchronousFileChannel newAsynchronousFileChannel(Path path, 310 Set<? extends OpenOption> options, 311 ExecutorService exec, 312 FileAttribute<?>... attrs) 313 throws IOException { 314 throw new UnsupportedOperationException(); 315 } 316 317 @Override 318 public SeekableByteChannel newByteChannel(Path path, 319 Set<? extends OpenOption> options, 320 FileAttribute<?>... attrs) 321 throws IOException { 322 return toAbstractJrtPath(path).newByteChannel(options, attrs); 323 } 324 325 @Override 326 public DirectoryStream<Path> newDirectoryStream( 327 Path path, Filter<? super Path> filter) throws IOException { 328 return toAbstractJrtPath(path).newDirectoryStream(filter); 329 } 330 331 @Override 332 public FileChannel newFileChannel(Path path, 333 Set<? extends OpenOption> options, 334 FileAttribute<?>... attrs) 335 throws IOException { 336 return toAbstractJrtPath(path).newFileChannel(options, attrs); 337 } 338 339 @Override 340 public InputStream newInputStream(Path path, OpenOption... options) 341 throws IOException { 342 return toAbstractJrtPath(path).newInputStream(options); 343 } 344 345 @Override 346 public OutputStream newOutputStream(Path path, OpenOption... options) 347 throws IOException { 348 return toAbstractJrtPath(path).newOutputStream(options); 349 } 350 351 @Override 352 @SuppressWarnings("unchecked") // Cast to A 353 public <A extends BasicFileAttributes> A 354 readAttributes(Path path, Class<A> type, LinkOption... options) 355 throws IOException { 356 if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) { 357 return (A) toAbstractJrtPath(path).getAttributes(options); 358 } 359 return null; 360 } 361 362 @Override 363 public Map<String, Object> 364 readAttributes(Path path, String attribute, LinkOption... options) 365 throws IOException { 366 return toAbstractJrtPath(path).readAttributes(attribute, options); 367 } 368 369 @Override 370 public void setAttribute(Path path, String attribute, 371 Object value, LinkOption... options) 372 throws IOException { 373 toAbstractJrtPath(path).setAttribute(attribute, value, options); 374 } 375} 376