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