author | sherman |
Fri, 15 Apr 2016 13:05:52 -0700 | |
changeset 37365 | 9cc4eb4d7491 |
parent 36511 | 9d0388c6b336 |
child 37614 | 99a3cdeb43a7 |
permissions | -rw-r--r-- |
27565 | 1 |
/* |
29270
0f65e3c44659
8073445: (fs) FileSystem.getPathMatcher(...) should check syntax component without regard to case
bpb
parents:
27565
diff
changeset
|
2 |
* Copyright (c) 2014, 2015, 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 |
||
37365 | 27 |
import java.io.ByteArrayInputStream; |
27565 | 28 |
import java.io.IOException; |
37365 | 29 |
import java.io.InputStream; |
30 |
import java.io.OutputStream; |
|
31 |
import java.nio.ByteBuffer; |
|
32 |
import java.nio.channels.Channels; |
|
33 |
import java.nio.channels.FileChannel; |
|
34 |
import java.nio.channels.NonWritableChannelException; |
|
35 |
import java.nio.channels.ReadableByteChannel; |
|
36 |
import java.nio.channels.SeekableByteChannel; |
|
37 |
import java.nio.file.ClosedFileSystemException; |
|
38 |
import java.nio.file.CopyOption; |
|
39 |
import java.nio.file.DirectoryStream; |
|
40 |
import java.nio.file.FileStore; |
|
41 |
import java.nio.file.FileSystem; |
|
27565 | 42 |
import java.nio.file.FileSystemException; |
36511 | 43 |
import java.nio.file.InvalidPathException; |
37365 | 44 |
import java.nio.file.LinkOption; |
27565 | 45 |
import java.nio.file.NoSuchFileException; |
46 |
import java.nio.file.NotDirectoryException; |
|
37365 | 47 |
import java.nio.file.OpenOption; |
27565 | 48 |
import java.nio.file.Path; |
37365 | 49 |
import java.nio.file.PathMatcher; |
50 |
import java.nio.file.ReadOnlyFileSystemException; |
|
51 |
import java.nio.file.StandardOpenOption; |
|
52 |
import java.nio.file.WatchService; |
|
53 |
import java.nio.file.attribute.FileAttribute; |
|
54 |
import java.nio.file.attribute.FileTime; |
|
55 |
import java.nio.file.attribute.UserPrincipalLookupService; |
|
56 |
import java.nio.file.spi.FileSystemProvider; |
|
27565 | 57 |
import java.util.ArrayList; |
58 |
import java.util.Arrays; |
|
37365 | 59 |
import java.util.Collections; |
60 |
import java.util.HashSet; |
|
27565 | 61 |
import java.util.Iterator; |
62 |
import java.util.Map; |
|
37365 | 63 |
import java.util.Objects; |
64 |
import java.util.Set; |
|
65 |
import java.util.regex.Pattern; |
|
66 |
import jdk.internal.jimage.ImageReader.Node; |
|
31673 | 67 |
import static java.util.stream.Collectors.toList; |
27565 | 68 |
|
69 |
/** |
|
36511 | 70 |
* jrt file system implementation built on System jimage files. |
71 |
* |
|
72 |
* @implNote This class needs to maintain JDK 8 source compatibility. |
|
73 |
* |
|
74 |
* It is used internally in the JDK to implement jimage/jrtfs access, |
|
75 |
* but also compiled and delivered as part of the jrtfs.jar to support access |
|
76 |
* to the jimage file provided by the shipped JDK by tools running on JDK 8. |
|
27565 | 77 |
*/ |
37365 | 78 |
class JrtFileSystem extends FileSystem { |
31673 | 79 |
|
37365 | 80 |
private final JrtFileSystemProvider provider; |
81 |
private final JrtPath rootPath = new JrtPath(this, "/"); |
|
27565 | 82 |
private volatile boolean isOpen; |
37365 | 83 |
private volatile boolean isClosable; |
84 |
private SystemImage image; |
|
27565 | 85 |
|
37365 | 86 |
JrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> env) |
87 |
throws IOException |
|
88 |
{ |
|
89 |
this.provider = provider; |
|
90 |
this.image = SystemImage.open(); // open image file |
|
91 |
this.isOpen = true; |
|
92 |
this.isClosable = env != null; |
|
27565 | 93 |
} |
94 |
||
36511 | 95 |
// FileSystem method implementations |
27565 | 96 |
@Override |
97 |
public boolean isOpen() { |
|
98 |
return isOpen; |
|
99 |
} |
|
100 |
||
101 |
@Override |
|
102 |
public void close() throws IOException { |
|
37365 | 103 |
if (!isClosable) |
104 |
throw new UnsupportedOperationException(); |
|
27565 | 105 |
cleanup(); |
106 |
} |
|
107 |
||
108 |
@Override |
|
36511 | 109 |
protected void finalize() throws Throwable { |
27565 | 110 |
try { |
111 |
cleanup(); |
|
37365 | 112 |
} catch (IOException ignored) {} |
36511 | 113 |
} |
114 |
||
115 |
@Override |
|
37365 | 116 |
public FileSystemProvider provider() { |
117 |
return provider; |
|
118 |
} |
|
119 |
||
120 |
@Override |
|
121 |
public Iterable<Path> getRootDirectories() { |
|
122 |
ArrayList<Path> dirs = new ArrayList<>(); |
|
123 |
dirs.add(getRootPath()); |
|
124 |
return dirs; |
|
36511 | 125 |
} |
126 |
||
127 |
@Override |
|
37365 | 128 |
public JrtPath getPath(String first, String... more) { |
129 |
if (more.length == 0) { |
|
130 |
return new JrtPath(this, first); |
|
131 |
} |
|
132 |
StringBuilder sb = new StringBuilder(); |
|
133 |
sb.append(first); |
|
134 |
for (String path : more) { |
|
135 |
if (path.length() > 0) { |
|
136 |
if (sb.length() > 0) { |
|
137 |
sb.append('/'); |
|
138 |
} |
|
139 |
sb.append(path); |
|
140 |
} |
|
141 |
} |
|
142 |
return new JrtPath(this, sb.toString()); |
|
143 |
} |
|
144 |
||
145 |
@Override |
|
146 |
public final boolean isReadOnly() { |
|
147 |
return true; |
|
148 |
} |
|
149 |
||
150 |
@Override |
|
151 |
public final UserPrincipalLookupService getUserPrincipalLookupService() { |
|
152 |
throw new UnsupportedOperationException(); |
|
153 |
} |
|
154 |
||
155 |
@Override |
|
156 |
public final WatchService newWatchService() { |
|
157 |
throw new UnsupportedOperationException(); |
|
36511 | 158 |
} |
159 |
||
160 |
@Override |
|
37365 | 161 |
public final Iterable<FileStore> getFileStores() { |
162 |
ArrayList<FileStore> list = new ArrayList<>(1); |
|
163 |
list.add(getFileStore(getRootPath())); |
|
164 |
return list; |
|
165 |
} |
|
166 |
||
167 |
private static final Set<String> supportedFileAttributeViews |
|
168 |
= Collections.unmodifiableSet( |
|
169 |
new HashSet<String>(Arrays.asList("basic", "jrt"))); |
|
170 |
||
171 |
@Override |
|
172 |
public final Set<String> supportedFileAttributeViews() { |
|
173 |
return supportedFileAttributeViews; |
|
174 |
} |
|
175 |
||
176 |
@Override |
|
177 |
public final String toString() { |
|
178 |
return "jrt:/"; |
|
179 |
} |
|
180 |
||
181 |
@Override |
|
182 |
public final String getSeparator() { |
|
183 |
return "/"; |
|
36511 | 184 |
} |
185 |
||
186 |
@Override |
|
37365 | 187 |
public PathMatcher getPathMatcher(String syntaxAndInput) { |
188 |
int pos = syntaxAndInput.indexOf(':'); |
|
189 |
if (pos <= 0 || pos == syntaxAndInput.length()) { |
|
190 |
throw new IllegalArgumentException(); |
|
191 |
} |
|
192 |
String syntax = syntaxAndInput.substring(0, pos); |
|
193 |
String input = syntaxAndInput.substring(pos + 1); |
|
194 |
String expr; |
|
195 |
if (syntax.equalsIgnoreCase("glob")) { |
|
196 |
expr = JrtUtils.toRegexPattern(input); |
|
197 |
} else if (syntax.equalsIgnoreCase("regex")) { |
|
198 |
expr = input; |
|
199 |
} else { |
|
200 |
throw new UnsupportedOperationException("Syntax '" + syntax |
|
201 |
+ "' not recognized"); |
|
202 |
} |
|
203 |
// return matcher |
|
204 |
final Pattern pattern = Pattern.compile(expr); |
|
205 |
return (Path path) -> pattern.matcher(path.toString()).matches(); |
|
206 |
} |
|
207 |
||
208 |
JrtPath resolveLink(JrtPath path) throws IOException { |
|
209 |
Node node = checkNode(path); |
|
36511 | 210 |
if (node.isLink()) { |
211 |
node = node.resolveLink(); |
|
37365 | 212 |
return new JrtPath(this, node.getName()); // TBD, normalized? |
36511 | 213 |
} |
37365 | 214 |
return path; |
36511 | 215 |
} |
216 |
||
37365 | 217 |
JrtFileAttributes getFileAttributes(JrtPath path, LinkOption... options) |
36511 | 218 |
throws IOException { |
37365 | 219 |
Node node = checkNode(path); |
36511 | 220 |
if (node.isLink() && followLinks(options)) { |
221 |
return new JrtFileAttributes(node.resolveLink(true)); |
|
222 |
} |
|
223 |
return new JrtFileAttributes(node); |
|
27565 | 224 |
} |
225 |
||
37365 | 226 |
/** |
227 |
* returns the list of child paths of the given directory "path" |
|
228 |
* |
|
229 |
* @param path name of the directory whose content is listed |
|
230 |
* @return iterator for child paths of the given directory path |
|
231 |
*/ |
|
232 |
Iterator<Path> iteratorOf(JrtPath path, DirectoryStream.Filter<? super Path> filter) |
|
233 |
throws IOException { |
|
234 |
Node node = checkNode(path).resolveLink(true); |
|
235 |
if (!node.isDirectory()) { |
|
236 |
throw new NotDirectoryException(path.getName()); |
|
237 |
} |
|
238 |
if (filter == null) { |
|
239 |
return node.getChildren() |
|
240 |
.stream() |
|
241 |
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) |
|
242 |
.iterator(); |
|
243 |
} |
|
244 |
return node.getChildren() |
|
245 |
.stream() |
|
246 |
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) |
|
247 |
.filter(p -> { try { return filter.accept(p); |
|
248 |
} catch (IOException x) {} |
|
249 |
return false; |
|
250 |
}) |
|
251 |
.iterator(); |
|
252 |
} |
|
253 |
||
254 |
// returns the content of the file resource specified by the path |
|
255 |
byte[] getFileContent(JrtPath path) throws IOException { |
|
256 |
Node node = checkNode(path); |
|
257 |
if (node.isDirectory()) { |
|
258 |
throw new FileSystemException(path + " is a directory"); |
|
259 |
} |
|
260 |
//assert node.isResource() : "resource node expected here"; |
|
261 |
return image.getResource(node); |
|
262 |
} |
|
263 |
||
264 |
/////////////// Implementation details below this point ////////// |
|
265 |
||
266 |
// static utility methods |
|
267 |
static ReadOnlyFileSystemException readOnly() { |
|
268 |
return new ReadOnlyFileSystemException(); |
|
269 |
} |
|
270 |
||
271 |
// do the supplied options imply that we have to chase symlinks? |
|
272 |
static boolean followLinks(LinkOption... options) { |
|
273 |
if (options != null) { |
|
274 |
for (LinkOption lo : options) { |
|
275 |
Objects.requireNonNull(lo); |
|
276 |
if (lo == LinkOption.NOFOLLOW_LINKS) { |
|
277 |
return false; |
|
278 |
} else { |
|
279 |
throw new AssertionError("should not reach here"); |
|
280 |
} |
|
281 |
} |
|
282 |
} |
|
283 |
return true; |
|
284 |
} |
|
285 |
||
286 |
// check that the options passed are supported by (read-only) jrt file system |
|
287 |
static void checkOptions(Set<? extends OpenOption> options) { |
|
288 |
// check for options of null type and option is an intance of StandardOpenOption |
|
289 |
for (OpenOption option : options) { |
|
290 |
Objects.requireNonNull(option); |
|
291 |
if (!(option instanceof StandardOpenOption)) { |
|
292 |
throw new IllegalArgumentException(); |
|
293 |
} |
|
294 |
} |
|
295 |
if (options.contains(StandardOpenOption.WRITE) || |
|
296 |
options.contains(StandardOpenOption.APPEND)) { |
|
297 |
throw readOnly(); |
|
298 |
} |
|
299 |
} |
|
300 |
||
301 |
// clean up this file system - called from finalize and close |
|
302 |
void cleanup() throws IOException { |
|
303 |
if (!isOpen) { |
|
304 |
return; |
|
305 |
} |
|
306 |
synchronized (this) { |
|
307 |
isOpen = false; |
|
308 |
// close image reader and null out |
|
309 |
image.close(); |
|
310 |
image = null; |
|
311 |
} |
|
312 |
} |
|
313 |
||
314 |
// These methods throw read only file system exception |
|
315 |
final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime) |
|
316 |
throws IOException { |
|
317 |
throw readOnly(); |
|
318 |
} |
|
319 |
||
320 |
// These methods throw read only file system exception |
|
321 |
final void createDirectory(JrtPath jrtPath, FileAttribute<?>... attrs) throws IOException { |
|
322 |
throw readOnly(); |
|
323 |
} |
|
324 |
||
325 |
final void deleteFile(JrtPath jrtPath, boolean failIfNotExists) |
|
326 |
throws IOException { |
|
327 |
throw readOnly(); |
|
328 |
} |
|
329 |
||
330 |
final OutputStream newOutputStream(JrtPath jrtPath, OpenOption... options) |
|
331 |
throws IOException { |
|
332 |
throw readOnly(); |
|
333 |
} |
|
334 |
||
335 |
final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOption... options) |
|
336 |
throws IOException { |
|
337 |
throw readOnly(); |
|
338 |
} |
|
339 |
||
340 |
final FileChannel newFileChannel(JrtPath path, |
|
341 |
Set<? extends OpenOption> options, |
|
342 |
FileAttribute<?>... attrs) |
|
343 |
throws IOException { |
|
344 |
throw new UnsupportedOperationException("newFileChannel"); |
|
345 |
} |
|
346 |
||
347 |
final InputStream newInputStream(JrtPath path) throws IOException { |
|
348 |
return new ByteArrayInputStream(getFileContent(path)); |
|
349 |
} |
|
350 |
||
351 |
final SeekableByteChannel newByteChannel(JrtPath path, |
|
352 |
Set<? extends OpenOption> options, |
|
353 |
FileAttribute<?>... attrs) |
|
354 |
throws IOException { |
|
355 |
checkOptions(options); |
|
356 |
||
357 |
byte[] buf = getFileContent(path); |
|
358 |
final ReadableByteChannel rbc |
|
359 |
= Channels.newChannel(new ByteArrayInputStream(buf)); |
|
360 |
final long size = buf.length; |
|
361 |
return new SeekableByteChannel() { |
|
362 |
long read = 0; |
|
363 |
||
364 |
@Override |
|
365 |
public boolean isOpen() { |
|
366 |
return rbc.isOpen(); |
|
367 |
} |
|
368 |
||
369 |
@Override |
|
370 |
public long position() throws IOException { |
|
371 |
return read; |
|
372 |
} |
|
373 |
||
374 |
@Override |
|
375 |
public SeekableByteChannel position(long pos) |
|
376 |
throws IOException { |
|
377 |
throw new UnsupportedOperationException(); |
|
378 |
} |
|
379 |
||
380 |
@Override |
|
381 |
public int read(ByteBuffer dst) throws IOException { |
|
382 |
int n = rbc.read(dst); |
|
383 |
if (n > 0) { |
|
384 |
read += n; |
|
385 |
} |
|
386 |
return n; |
|
387 |
} |
|
388 |
||
389 |
@Override |
|
390 |
public SeekableByteChannel truncate(long size) |
|
391 |
throws IOException { |
|
392 |
throw new NonWritableChannelException(); |
|
393 |
} |
|
394 |
||
395 |
@Override |
|
396 |
public int write(ByteBuffer src) throws IOException { |
|
397 |
throw new NonWritableChannelException(); |
|
398 |
} |
|
399 |
||
400 |
@Override |
|
401 |
public long size() throws IOException { |
|
402 |
return size; |
|
403 |
} |
|
404 |
||
405 |
@Override |
|
406 |
public void close() throws IOException { |
|
407 |
rbc.close(); |
|
408 |
} |
|
409 |
}; |
|
410 |
} |
|
411 |
||
412 |
final JrtFileStore getFileStore(JrtPath path) { |
|
413 |
return new JrtFileStore(path); |
|
414 |
} |
|
415 |
||
416 |
final void ensureOpen() throws IOException { |
|
417 |
if (!isOpen()) { |
|
418 |
throw new ClosedFileSystemException(); |
|
419 |
} |
|
420 |
} |
|
421 |
||
422 |
final JrtPath getRootPath() { |
|
423 |
return rootPath; |
|
424 |
} |
|
425 |
||
426 |
boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException { |
|
427 |
return checkNode(path1) == checkNode(path2); |
|
428 |
} |
|
429 |
||
430 |
boolean isLink(JrtPath path) throws IOException { |
|
431 |
return checkNode(path).isLink(); |
|
432 |
} |
|
433 |
||
434 |
boolean exists(JrtPath path) throws IOException { |
|
36511 | 435 |
try { |
37365 | 436 |
checkNode(path); |
36511 | 437 |
} catch (NoSuchFileException exp) { |
438 |
return false; |
|
439 |
} |
|
440 |
return true; |
|
441 |
} |
|
442 |
||
37365 | 443 |
boolean isDirectory(JrtPath path, boolean resolveLinks) |
36511 | 444 |
throws IOException { |
37365 | 445 |
Node node = checkNode(path); |
36511 | 446 |
return resolveLinks && node.isLink() |
447 |
? node.resolveLink(true).isDirectory() |
|
448 |
: node.isDirectory(); |
|
449 |
} |
|
450 |
||
37365 | 451 |
JrtPath toRealPath(JrtPath path, LinkOption... options) |
452 |
throws IOException { |
|
453 |
Node node = checkNode(path); |
|
454 |
if (followLinks(options) && node.isLink()) { |
|
455 |
node = node.resolveLink(); |
|
36511 | 456 |
} |
37365 | 457 |
// image node holds the real/absolute path name |
458 |
return new JrtPath(this, node.getName(), true); |
|
36511 | 459 |
} |
460 |
||
37365 | 461 |
private Node lookup(String path) { |
462 |
try { |
|
463 |
return image.findNode(path); |
|
464 |
} catch (RuntimeException re) { |
|
465 |
throw new InvalidPathException(path, re.toString()); |
|
27565 | 466 |
} |
467 |
} |
|
468 |
||
37365 | 469 |
private Node lookupSymbolic(String path) { |
470 |
int i = 1; |
|
471 |
while (i < path.length()) { |
|
472 |
i = path.indexOf('/', i); |
|
473 |
if (i == -1) { |
|
474 |
break; |
|
475 |
} |
|
476 |
String prefix = path.substring(0, i); |
|
477 |
Node node = lookup(prefix); |
|
478 |
if (node == null) { |
|
479 |
break; |
|
480 |
} |
|
481 |
if (node.isLink()) { |
|
482 |
Node link = node.resolveLink(true); |
|
483 |
// resolved symbolic path concatenated to the rest of the path |
|
484 |
String resPath = link.getName() + path.substring(i); |
|
485 |
node = lookup(resPath); |
|
486 |
return node != null ? node : lookupSymbolic(resPath); |
|
487 |
} |
|
488 |
i++; |
|
27565 | 489 |
} |
31673 | 490 |
return null; |
491 |
} |
|
492 |
||
37365 | 493 |
Node checkNode(JrtPath path) throws IOException { |
494 |
ensureOpen(); |
|
495 |
String p = path.getResolvedPath(); |
|
496 |
Node node = lookup(p); |
|
36511 | 497 |
if (node == null) { |
37365 | 498 |
node = lookupSymbolic(p); |
36511 | 499 |
if (node == null) { |
37365 | 500 |
throw new NoSuchFileException(p); |
31673 | 501 |
} |
502 |
} |
|
36511 | 503 |
return node; |
27565 | 504 |
} |
505 |
} |