author | sherman |
Mon, 18 Apr 2016 10:57:11 -0700 | |
changeset 37516 | 66a0579e606c |
parent 37365 | 9cc4eb4d7491 |
child 37782 | ad8fe7507ecc |
permissions | -rw-r--r-- |
27565 | 1 |
/* |
37365 | 2 |
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. |
27565 | 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.*; |
|
36511 | 28 |
import java.net.MalformedURLException; |
29 |
import java.net.URL; |
|
30 |
import java.net.URLClassLoader; |
|
27565 | 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; |
|
36511 | 37 |
import java.security.AccessController; |
38 |
import java.security.PrivilegedAction; |
|
27565 | 39 |
import java.util.HashMap; |
40 |
import java.util.Map; |
|
36511 | 41 |
import java.util.Objects; |
27565 | 42 |
import java.util.Set; |
43 |
import java.util.concurrent.ExecutorService; |
|
44 |
||
36511 | 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 |
*/ |
|
27565 | 55 |
public final class JrtFileSystemProvider extends FileSystemProvider { |
36511 | 56 |
|
27565 | 57 |
private volatile FileSystem theFileSystem; |
58 |
||
36511 | 59 |
public JrtFileSystemProvider() { |
60 |
} |
|
27565 | 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) { |
|
37365 | 73 |
String home = SystemImage.RUNTIME_HOME; |
36511 | 74 |
FilePermission perm |
75 |
= new FilePermission(home + File.separator + "-", "read"); |
|
27565 | 76 |
sm.checkPermission(perm); |
77 |
} |
|
78 |
} |
|
79 |
||
80 |
private void checkUri(URI uri) { |
|
36511 | 81 |
if (!uri.getScheme().equalsIgnoreCase(getScheme())) { |
27565 | 82 |
throw new IllegalArgumentException("URI does not match this provider"); |
36511 | 83 |
} |
84 |
if (uri.getAuthority() != null) { |
|
27565 | 85 |
throw new IllegalArgumentException("Authority component present"); |
36511 | 86 |
} |
87 |
if (uri.getPath() == null) { |
|
27565 | 88 |
throw new IllegalArgumentException("Path component is undefined"); |
36511 | 89 |
} |
90 |
if (!uri.getPath().equals("/")) { |
|
27565 | 91 |
throw new IllegalArgumentException("Path component should be '/'"); |
36511 | 92 |
} |
93 |
if (uri.getQuery() != null) { |
|
27565 | 94 |
throw new IllegalArgumentException("Query component present"); |
36511 | 95 |
} |
96 |
if (uri.getFragment() != null) { |
|
27565 | 97 |
throw new IllegalArgumentException("Fragment component present"); |
36511 | 98 |
} |
27565 | 99 |
} |
100 |
||
101 |
@Override |
|
102 |
public FileSystem newFileSystem(URI uri, Map<String, ?> env) |
|
36511 | 103 |
throws IOException { |
37516
66a0579e606c
8154403: JRT filesystem loaded by JDK8 with URLClassLoader is not closable since JDK-8147460
sherman
parents:
37365
diff
changeset
|
104 |
Objects.requireNonNull(env); |
27565 | 105 |
checkPermission(); |
106 |
checkUri(uri); |
|
37516
66a0579e606c
8154403: JRT filesystem loaded by JDK8 with URLClassLoader is not closable since JDK-8147460
sherman
parents:
37365
diff
changeset
|
107 |
if (env.containsKey("java.home")) { |
36511 | 108 |
return newFileSystem((String)env.get("java.home"), uri, env); |
109 |
} else { |
|
37365 | 110 |
return new JrtFileSystem(this, env); |
36511 | 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 |
); |
|
27565 | 175 |
} |
176 |
||
177 |
@Override |
|
178 |
public Path getPath(URI uri) { |
|
179 |
checkPermission(); |
|
36511 | 180 |
if (!uri.getScheme().equalsIgnoreCase(getScheme())) { |
27565 | 181 |
throw new IllegalArgumentException("URI does not match this provider"); |
36511 | 182 |
} |
183 |
if (uri.getAuthority() != null) { |
|
27565 | 184 |
throw new IllegalArgumentException("Authority component present"); |
36511 | 185 |
} |
186 |
if (uri.getQuery() != null) { |
|
27565 | 187 |
throw new IllegalArgumentException("Query component present"); |
36511 | 188 |
} |
189 |
if (uri.getFragment() != null) { |
|
27565 | 190 |
throw new IllegalArgumentException("Fragment component present"); |
36511 | 191 |
} |
27565 | 192 |
String path = uri.getPath(); |
36511 | 193 |
if (path == null || path.charAt(0) != '/') { |
27565 | 194 |
throw new IllegalArgumentException("Invalid path component"); |
36511 | 195 |
} |
27565 | 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 { |
|
37365 | 207 |
this.theFileSystem = fs = new JrtFileSystem(this, null); |
27565 | 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 |
|
37365 | 225 |
static final JrtPath toJrtPath(Path path) { |
226 |
Objects.requireNonNull(path, "path"); |
|
227 |
if (!(path instanceof JrtPath)) { |
|
27565 | 228 |
throw new ProviderMismatchException(); |
36511 | 229 |
} |
37365 | 230 |
return (JrtPath) path; |
27565 | 231 |
} |
232 |
||
233 |
@Override |
|
234 |
public void checkAccess(Path path, AccessMode... modes) throws IOException { |
|
37365 | 235 |
toJrtPath(path).checkAccess(modes); |
27565 | 236 |
} |
237 |
||
238 |
@Override |
|
31673 | 239 |
public Path readSymbolicLink(Path link) throws IOException { |
37365 | 240 |
return toJrtPath(link).readSymbolicLink(); |
31673 | 241 |
} |
242 |
||
243 |
@Override |
|
27565 | 244 |
public void copy(Path src, Path target, CopyOption... options) |
36511 | 245 |
throws IOException { |
37365 | 246 |
toJrtPath(src).copy(toJrtPath(target), options); |
27565 | 247 |
} |
248 |
||
249 |
@Override |
|
250 |
public void createDirectory(Path path, FileAttribute<?>... attrs) |
|
36511 | 251 |
throws IOException { |
37365 | 252 |
toJrtPath(path).createDirectory(attrs); |
27565 | 253 |
} |
254 |
||
255 |
@Override |
|
256 |
public final void delete(Path path) throws IOException { |
|
37365 | 257 |
toJrtPath(path).delete(); |
27565 | 258 |
} |
259 |
||
260 |
@Override |
|
261 |
@SuppressWarnings("unchecked") |
|
262 |
public <V extends FileAttributeView> V |
|
36511 | 263 |
getFileAttributeView(Path path, Class<V> type, LinkOption... options) { |
37365 | 264 |
return JrtFileAttributeView.get(toJrtPath(path), type, options); |
27565 | 265 |
} |
266 |
||
267 |
@Override |
|
268 |
public FileStore getFileStore(Path path) throws IOException { |
|
37365 | 269 |
return toJrtPath(path).getFileStore(); |
27565 | 270 |
} |
271 |
||
272 |
@Override |
|
273 |
public boolean isHidden(Path path) { |
|
37365 | 274 |
return toJrtPath(path).isHidden(); |
27565 | 275 |
} |
276 |
||
277 |
@Override |
|
278 |
public boolean isSameFile(Path path, Path other) throws IOException { |
|
37365 | 279 |
return toJrtPath(path).isSameFile(other); |
27565 | 280 |
} |
281 |
||
282 |
@Override |
|
283 |
public void move(Path src, Path target, CopyOption... options) |
|
36511 | 284 |
throws IOException { |
37365 | 285 |
toJrtPath(src).move(toJrtPath(target), options); |
27565 | 286 |
} |
287 |
||
288 |
@Override |
|
289 |
public AsynchronousFileChannel newAsynchronousFileChannel(Path path, |
|
290 |
Set<? extends OpenOption> options, |
|
291 |
ExecutorService exec, |
|
292 |
FileAttribute<?>... attrs) |
|
36511 | 293 |
throws IOException { |
27565 | 294 |
throw new UnsupportedOperationException(); |
295 |
} |
|
296 |
||
297 |
@Override |
|
298 |
public SeekableByteChannel newByteChannel(Path path, |
|
36511 | 299 |
Set<? extends OpenOption> options, |
300 |
FileAttribute<?>... attrs) |
|
301 |
throws IOException { |
|
37365 | 302 |
return toJrtPath(path).newByteChannel(options, attrs); |
27565 | 303 |
} |
304 |
||
305 |
@Override |
|
306 |
public DirectoryStream<Path> newDirectoryStream( |
|
36511 | 307 |
Path path, Filter<? super Path> filter) throws IOException { |
37365 | 308 |
return toJrtPath(path).newDirectoryStream(filter); |
27565 | 309 |
} |
310 |
||
311 |
@Override |
|
312 |
public FileChannel newFileChannel(Path path, |
|
36511 | 313 |
Set<? extends OpenOption> options, |
314 |
FileAttribute<?>... attrs) |
|
315 |
throws IOException { |
|
37365 | 316 |
return toJrtPath(path).newFileChannel(options, attrs); |
27565 | 317 |
} |
318 |
||
319 |
@Override |
|
320 |
public InputStream newInputStream(Path path, OpenOption... options) |
|
36511 | 321 |
throws IOException { |
37365 | 322 |
return toJrtPath(path).newInputStream(options); |
27565 | 323 |
} |
324 |
||
325 |
@Override |
|
326 |
public OutputStream newOutputStream(Path path, OpenOption... options) |
|
36511 | 327 |
throws IOException { |
37365 | 328 |
return toJrtPath(path).newOutputStream(options); |
27565 | 329 |
} |
330 |
||
331 |
@Override |
|
332 |
@SuppressWarnings("unchecked") // Cast to A |
|
333 |
public <A extends BasicFileAttributes> A |
|
36511 | 334 |
readAttributes(Path path, Class<A> type, LinkOption... options) |
335 |
throws IOException { |
|
336 |
if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) { |
|
37365 | 337 |
return (A) toJrtPath(path).getAttributes(options); |
36511 | 338 |
} |
27565 | 339 |
return null; |
340 |
} |
|
341 |
||
342 |
@Override |
|
343 |
public Map<String, Object> |
|
36511 | 344 |
readAttributes(Path path, String attribute, LinkOption... options) |
345 |
throws IOException { |
|
37365 | 346 |
return toJrtPath(path).readAttributes(attribute, options); |
27565 | 347 |
} |
348 |
||
349 |
@Override |
|
350 |
public void setAttribute(Path path, String attribute, |
|
36511 | 351 |
Object value, LinkOption... options) |
352 |
throws IOException { |
|
37365 | 353 |
toJrtPath(path).setAttribute(attribute, value, options); |
27565 | 354 |
} |
355 |
} |