JrtFileSystemProvider.java revision 14178:60fdfede6d80
1/* 2 * Copyright (c) 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. 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 = SystemImage.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 new JrtFileSystem(this, env); 111 } 112 } 113 114 private static final String JRT_FS_JAR = "jrt-fs.jar"; 115 private FileSystem newFileSystem(String targetHome, URI uri, Map<String, ?> env) 116 throws IOException { 117 Objects.requireNonNull(targetHome); 118 Path jrtfs = FileSystems.getDefault().getPath(targetHome, JRT_FS_JAR); 119 if (Files.notExists(jrtfs)) { 120 throw new IOException(jrtfs.toString() + " not exist"); 121 } 122 Map<String,?> newEnv = new HashMap<>(env); 123 newEnv.remove("java.home"); 124 ClassLoader cl = newJrtFsLoader(jrtfs); 125 try { 126 Class<?> c = Class.forName(JrtFileSystemProvider.class.getName(), false, cl); 127 return ((FileSystemProvider)c.newInstance()).newFileSystem(uri, newEnv); 128 } catch (ClassNotFoundException | 129 IllegalAccessException | 130 InstantiationException e) { 131 throw new IOException(e); 132 } 133 } 134 135 private static class JrtFsLoader extends URLClassLoader { 136 JrtFsLoader(URL[] urls) { 137 super(urls); 138 } 139 @Override 140 protected Class<?> loadClass(String cn, boolean resolve) 141 throws ClassNotFoundException 142 { 143 Class<?> c = findLoadedClass(cn); 144 if (c == null) { 145 URL u = findResource(cn.replace('.', '/') + ".class"); 146 if (u != null) { 147 c = findClass(cn); 148 } else { 149 return super.loadClass(cn, resolve); 150 } 151 } 152 if (resolve) 153 resolveClass(c); 154 return c; 155 } 156 } 157 158 private static URLClassLoader newJrtFsLoader(Path jrtfs) { 159 final URL url; 160 try { 161 url = jrtfs.toUri().toURL(); 162 } catch (MalformedURLException mue) { 163 throw new IllegalArgumentException(mue); 164 } 165 166 final URL[] urls = new URL[] { url }; 167 return AccessController.doPrivileged( 168 new PrivilegedAction<URLClassLoader>() { 169 @Override 170 public URLClassLoader run() { 171 return new JrtFsLoader(urls); 172 } 173 } 174 ); 175 } 176 177 @Override 178 public Path getPath(URI uri) { 179 checkPermission(); 180 if (!uri.getScheme().equalsIgnoreCase(getScheme())) { 181 throw new IllegalArgumentException("URI does not match this provider"); 182 } 183 if (uri.getAuthority() != null) { 184 throw new IllegalArgumentException("Authority component present"); 185 } 186 if (uri.getQuery() != null) { 187 throw new IllegalArgumentException("Query component present"); 188 } 189 if (uri.getFragment() != null) { 190 throw new IllegalArgumentException("Fragment component present"); 191 } 192 String path = uri.getPath(); 193 if (path == null || path.charAt(0) != '/') { 194 throw new IllegalArgumentException("Invalid path component"); 195 } 196 return getTheFileSystem().getPath(path); 197 } 198 199 private FileSystem getTheFileSystem() { 200 checkPermission(); 201 FileSystem fs = this.theFileSystem; 202 if (fs == null) { 203 synchronized (this) { 204 fs = this.theFileSystem; 205 if (fs == null) { 206 try { 207 this.theFileSystem = fs = new JrtFileSystem(this, null); 208 } catch (IOException ioe) { 209 throw new InternalError(ioe); 210 } 211 } 212 } 213 } 214 return fs; 215 } 216 217 @Override 218 public FileSystem getFileSystem(URI uri) { 219 checkPermission(); 220 checkUri(uri); 221 return getTheFileSystem(); 222 } 223 224 // Checks that the given file is a JrtPath 225 static final JrtPath toJrtPath(Path path) { 226 Objects.requireNonNull(path, "path"); 227 if (!(path instanceof JrtPath)) { 228 throw new ProviderMismatchException(); 229 } 230 return (JrtPath) path; 231 } 232 233 @Override 234 public void checkAccess(Path path, AccessMode... modes) throws IOException { 235 toJrtPath(path).checkAccess(modes); 236 } 237 238 @Override 239 public Path readSymbolicLink(Path link) throws IOException { 240 return toJrtPath(link).readSymbolicLink(); 241 } 242 243 @Override 244 public void copy(Path src, Path target, CopyOption... options) 245 throws IOException { 246 toJrtPath(src).copy(toJrtPath(target), options); 247 } 248 249 @Override 250 public void createDirectory(Path path, FileAttribute<?>... attrs) 251 throws IOException { 252 toJrtPath(path).createDirectory(attrs); 253 } 254 255 @Override 256 public final void delete(Path path) throws IOException { 257 toJrtPath(path).delete(); 258 } 259 260 @Override 261 @SuppressWarnings("unchecked") 262 public <V extends FileAttributeView> V 263 getFileAttributeView(Path path, Class<V> type, LinkOption... options) { 264 return JrtFileAttributeView.get(toJrtPath(path), type, options); 265 } 266 267 @Override 268 public FileStore getFileStore(Path path) throws IOException { 269 return toJrtPath(path).getFileStore(); 270 } 271 272 @Override 273 public boolean isHidden(Path path) { 274 return toJrtPath(path).isHidden(); 275 } 276 277 @Override 278 public boolean isSameFile(Path path, Path other) throws IOException { 279 return toJrtPath(path).isSameFile(other); 280 } 281 282 @Override 283 public void move(Path src, Path target, CopyOption... options) 284 throws IOException { 285 toJrtPath(src).move(toJrtPath(target), options); 286 } 287 288 @Override 289 public AsynchronousFileChannel newAsynchronousFileChannel(Path path, 290 Set<? extends OpenOption> options, 291 ExecutorService exec, 292 FileAttribute<?>... attrs) 293 throws IOException { 294 throw new UnsupportedOperationException(); 295 } 296 297 @Override 298 public SeekableByteChannel newByteChannel(Path path, 299 Set<? extends OpenOption> options, 300 FileAttribute<?>... attrs) 301 throws IOException { 302 return toJrtPath(path).newByteChannel(options, attrs); 303 } 304 305 @Override 306 public DirectoryStream<Path> newDirectoryStream( 307 Path path, Filter<? super Path> filter) throws IOException { 308 return toJrtPath(path).newDirectoryStream(filter); 309 } 310 311 @Override 312 public FileChannel newFileChannel(Path path, 313 Set<? extends OpenOption> options, 314 FileAttribute<?>... attrs) 315 throws IOException { 316 return toJrtPath(path).newFileChannel(options, attrs); 317 } 318 319 @Override 320 public InputStream newInputStream(Path path, OpenOption... options) 321 throws IOException { 322 return toJrtPath(path).newInputStream(options); 323 } 324 325 @Override 326 public OutputStream newOutputStream(Path path, OpenOption... options) 327 throws IOException { 328 return toJrtPath(path).newOutputStream(options); 329 } 330 331 @Override 332 @SuppressWarnings("unchecked") // Cast to A 333 public <A extends BasicFileAttributes> A 334 readAttributes(Path path, Class<A> type, LinkOption... options) 335 throws IOException { 336 if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) { 337 return (A) toJrtPath(path).getAttributes(options); 338 } 339 return null; 340 } 341 342 @Override 343 public Map<String, Object> 344 readAttributes(Path path, String attribute, LinkOption... options) 345 throws IOException { 346 return toJrtPath(path).readAttributes(attribute, options); 347 } 348 349 @Override 350 public void setAttribute(Path path, String attribute, 351 Object value, LinkOption... options) 352 throws IOException { 353 toJrtPath(path).setAttribute(attribute, value, options); 354 } 355} 356