src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java
Print this page
@@ -22,310 +22,484 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.jrtfs;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.nio.file.LinkOption;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.NonWritableChannelException;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.ClosedFileSystemException;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
import java.nio.file.FileSystemException;
import java.nio.file.InvalidPathException;
+import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
+import java.nio.file.OpenOption;
import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.ReadOnlyFileSystemException;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.UserPrincipalLookupService;
+import java.nio.file.spi.FileSystemProvider;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
-import java.util.function.Function;
-import static java.util.stream.Collectors.toList;
-import jdk.internal.jimage.ImageReader;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Pattern;
import jdk.internal.jimage.ImageReader.Node;
-
+import static java.util.stream.Collectors.toList;
/**
* jrt file system implementation built on System jimage files.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
-class JrtFileSystem extends AbstractJrtFileSystem {
+class JrtFileSystem extends FileSystem {
- // System image reader
- private ImageReader bootImage;
- // root path
- private final JrtPath rootPath;
+ private final JrtFileSystemProvider provider;
+ private final JrtPath rootPath = new JrtPath(this, "/");
private volatile boolean isOpen;
+ private volatile boolean isClosable;
+ private SystemImage image;
- // open a .jimage and build directory structure
- private static ImageReader openImage(Path path) throws IOException {
- ImageReader image = ImageReader.open(path);
- image.getRootDirectory();
- return image;
- }
-
- JrtFileSystem(JrtFileSystemProvider provider,
- Map<String, ?> env)
- throws IOException {
- super(provider, env);
- checkExists(SystemImages.moduleImageFile());
-
- // open image file
- this.bootImage = openImage(SystemImages.moduleImageFile());
-
- byte[] root = new byte[]{'/'};
- rootPath = new JrtPath(this, root);
- isOpen = true;
+ JrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> env)
+ throws IOException
+ {
+ this.provider = provider;
+ this.image = SystemImage.open(); // open image file
+ this.isOpen = true;
+ this.isClosable = env != null;
}
// FileSystem method implementations
@Override
public boolean isOpen() {
return isOpen;
}
@Override
public void close() throws IOException {
+ if (!isClosable)
+ throw new UnsupportedOperationException();
cleanup();
}
@Override
protected void finalize() throws Throwable {
try {
cleanup();
- } catch (IOException ignored) {
+ } catch (IOException ignored) {}
}
- super.finalize();
+
+ @Override
+ public FileSystemProvider provider() {
+ return provider;
}
- // AbstractJrtFileSystem method implementations
@Override
- JrtPath getRootPath() {
- return rootPath;
+ public Iterable<Path> getRootDirectories() {
+ ArrayList<Path> dirs = new ArrayList<>();
+ dirs.add(getRootPath());
+ return dirs;
}
@Override
- boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException {
- ensureOpen();
- Node node1 = findNode(p1);
- Node node2 = findNode(p2);
- return node1.equals(node2);
+ public JrtPath getPath(String first, String... more) {
+ if (more.length == 0) {
+ return new JrtPath(this, first);
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(first);
+ for (String path : more) {
+ if (path.length() > 0) {
+ if (sb.length() > 0) {
+ sb.append('/');
+ }
+ sb.append(path);
+ }
+ }
+ return new JrtPath(this, sb.toString());
}
@Override
- boolean isLink(AbstractJrtPath jrtPath) throws IOException {
- return checkNode(jrtPath).isLink();
+ public final boolean isReadOnly() {
+ return true;
}
@Override
- AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
- Node node = checkNode(jrtPath);
- if (node.isLink()) {
- node = node.resolveLink();
- return toJrtPath(getBytes(node.getName()));
+ public final UserPrincipalLookupService getUserPrincipalLookupService() {
+ throw new UnsupportedOperationException();
}
- return jrtPath;
+ @Override
+ public final WatchService newWatchService() {
+ throw new UnsupportedOperationException();
}
@Override
- JrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options)
- throws IOException {
- Node node = checkNode(jrtPath);
- if (node.isLink() && followLinks(options)) {
- return new JrtFileAttributes(node.resolveLink(true));
+ public final Iterable<FileStore> getFileStores() {
+ ArrayList<FileStore> list = new ArrayList<>(1);
+ list.add(getFileStore(getRootPath()));
+ return list;
}
- return new JrtFileAttributes(node);
+
+ private static final Set<String> supportedFileAttributeViews
+ = Collections.unmodifiableSet(
+ new HashSet<String>(Arrays.asList("basic", "jrt")));
+
+ @Override
+ public final Set<String> supportedFileAttributeViews() {
+ return supportedFileAttributeViews;
}
@Override
- boolean exists(AbstractJrtPath jrtPath) throws IOException {
- try {
- checkNode(jrtPath);
- } catch (NoSuchFileException exp) {
- return false;
+ public final String toString() {
+ return "jrt:/";
}
- return true;
+
+ @Override
+ public final String getSeparator() {
+ return "/";
}
@Override
- boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks)
+ public PathMatcher getPathMatcher(String syntaxAndInput) {
+ int pos = syntaxAndInput.indexOf(':');
+ if (pos <= 0 || pos == syntaxAndInput.length()) {
+ throw new IllegalArgumentException();
+ }
+ String syntax = syntaxAndInput.substring(0, pos);
+ String input = syntaxAndInput.substring(pos + 1);
+ String expr;
+ if (syntax.equalsIgnoreCase("glob")) {
+ expr = JrtUtils.toRegexPattern(input);
+ } else if (syntax.equalsIgnoreCase("regex")) {
+ expr = input;
+ } else {
+ throw new UnsupportedOperationException("Syntax '" + syntax
+ + "' not recognized");
+ }
+ // return matcher
+ final Pattern pattern = Pattern.compile(expr);
+ return (Path path) -> pattern.matcher(path.toString()).matches();
+ }
+
+ JrtPath resolveLink(JrtPath path) throws IOException {
+ Node node = checkNode(path);
+ if (node.isLink()) {
+ node = node.resolveLink();
+ return new JrtPath(this, node.getName()); // TBD, normalized?
+ }
+ return path;
+ }
+
+ JrtFileAttributes getFileAttributes(JrtPath path, LinkOption... options)
throws IOException {
- Node node = checkNode(jrtPath);
- return resolveLinks && node.isLink()
- ? node.resolveLink(true).isDirectory()
- : node.isDirectory();
+ Node node = checkNode(path);
+ if (node.isLink() && followLinks(options)) {
+ return new JrtFileAttributes(node.resolveLink(true));
+ }
+ return new JrtFileAttributes(node);
}
- @Override
- Iterator<Path> iteratorOf(AbstractJrtPath jrtPath) throws IOException {
- Node node = checkNode(jrtPath).resolveLink(true);
+ /**
+ * returns the list of child paths of the given directory "path"
+ *
+ * @param path name of the directory whose content is listed
+ * @return iterator for child paths of the given directory path
+ */
+ Iterator<Path> iteratorOf(JrtPath path, DirectoryStream.Filter<? super Path> filter)
+ throws IOException {
+ Node node = checkNode(path).resolveLink(true);
if (!node.isDirectory()) {
- throw new NotDirectoryException(getString(jrtPath.getName()));
+ throw new NotDirectoryException(path.getName());
+ }
+ if (filter == null) {
+ return node.getChildren()
+ .stream()
+ .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
+ .iterator();
+ }
+ return node.getChildren()
+ .stream()
+ .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
+ .filter(p -> { try { return filter.accept(p);
+ } catch (IOException x) {}
+ return false;
+ })
+ .iterator();
}
- if (node.isRootDir()) {
- return rootDirIterator(jrtPath);
- } else if (node.isModulesDir()) {
- return modulesDirIterator(jrtPath);
- } else if (node.isPackagesDir()) {
- return packagesDirIterator(jrtPath);
+ // returns the content of the file resource specified by the path
+ byte[] getFileContent(JrtPath path) throws IOException {
+ Node node = checkNode(path);
+ if (node.isDirectory()) {
+ throw new FileSystemException(path + " is a directory");
+ }
+ //assert node.isResource() : "resource node expected here";
+ return image.getResource(node);
}
- return nodesToIterator(jrtPath, node.getChildren());
+ /////////////// Implementation details below this point //////////
+
+ // static utility methods
+ static ReadOnlyFileSystemException readOnly() {
+ return new ReadOnlyFileSystemException();
}
- @Override
- byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException {
- final Node node = checkResource(jrtPath);
- return bootImage.getResource(node);
+ // do the supplied options imply that we have to chase symlinks?
+ static boolean followLinks(LinkOption... options) {
+ if (options != null) {
+ for (LinkOption lo : options) {
+ Objects.requireNonNull(lo);
+ if (lo == LinkOption.NOFOLLOW_LINKS) {
+ return false;
+ } else {
+ throw new AssertionError("should not reach here");
+ }
+ }
+ }
+ return true;
+ }
+
+ // check that the options passed are supported by (read-only) jrt file system
+ static void checkOptions(Set<? extends OpenOption> options) {
+ // check for options of null type and option is an intance of StandardOpenOption
+ for (OpenOption option : options) {
+ Objects.requireNonNull(option);
+ if (!(option instanceof StandardOpenOption)) {
+ throw new IllegalArgumentException();
+ }
+ }
+ if (options.contains(StandardOpenOption.WRITE) ||
+ options.contains(StandardOpenOption.APPEND)) {
+ throw readOnly();
+ }
}
- // Implementation details below this point
// clean up this file system - called from finalize and close
- private void cleanup() throws IOException {
+ void cleanup() throws IOException {
if (!isOpen) {
return;
}
-
synchronized (this) {
isOpen = false;
-
- // close all image reader and null out
- bootImage.close();
- bootImage = null;
+ // close image reader and null out
+ image.close();
+ image = null;
}
}
- private Node lookup(byte[] path) {
- Node node = null;
- try {
- node = bootImage.findNode(getString(path));
- } catch (RuntimeException re) {
- throw new InvalidPathException(getString(path), re.toString());
+ // These methods throw read only file system exception
+ final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime)
+ throws IOException {
+ throw readOnly();
}
- return node;
+
+ // These methods throw read only file system exception
+ final void createDirectory(JrtPath jrtPath, FileAttribute<?>... attrs) throws IOException {
+ throw readOnly();
}
- private Node lookupSymbolic(byte[] path) {
- for (int i = 1; i < path.length; i++) {
- if (path[i] == (byte) '/') {
- byte[] prefix = Arrays.copyOfRange(path, 0, i);
- Node node = lookup(prefix);
- if (node == null) {
- break;
+ final void deleteFile(JrtPath jrtPath, boolean failIfNotExists)
+ throws IOException {
+ throw readOnly();
}
- if (node.isLink()) {
- Node link = node.resolveLink(true);
- // resolved symbolic path concatenated to the rest of the path
- String resPath = link.getName() + getString(path).substring(i);
- byte[] resPathBytes = getBytes(resPath);
- node = lookup(resPathBytes);
- return node != null ? node : lookupSymbolic(resPathBytes);
+ final OutputStream newOutputStream(JrtPath jrtPath, OpenOption... options)
+ throws IOException {
+ throw readOnly();
}
+
+ final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOption... options)
+ throws IOException {
+ throw readOnly();
}
+
+ final FileChannel newFileChannel(JrtPath path,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException {
+ throw new UnsupportedOperationException("newFileChannel");
}
- return null;
+ final InputStream newInputStream(JrtPath path) throws IOException {
+ return new ByteArrayInputStream(getFileContent(path));
}
- private Node findNode(AbstractJrtPath jrtPath) throws IOException {
- return findNode(jrtPath.getResolvedPath());
+ final SeekableByteChannel newByteChannel(JrtPath path,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException {
+ checkOptions(options);
+
+ byte[] buf = getFileContent(path);
+ final ReadableByteChannel rbc
+ = Channels.newChannel(new ByteArrayInputStream(buf));
+ final long size = buf.length;
+ return new SeekableByteChannel() {
+ long read = 0;
+
+ @Override
+ public boolean isOpen() {
+ return rbc.isOpen();
}
- private Node findNode(byte[] path) throws IOException {
- Node node = lookup(path);
- if (node == null) {
- node = lookupSymbolic(path);
- if (node == null) {
- throw new NoSuchFileException(getString(path));
+ @Override
+ public long position() throws IOException {
+ return read;
+ }
+
+ @Override
+ public SeekableByteChannel position(long pos)
+ throws IOException {
+ throw new UnsupportedOperationException();
}
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ int n = rbc.read(dst);
+ if (n > 0) {
+ read += n;
}
- return node;
+ return n;
}
- private Node checkNode(AbstractJrtPath jrtPath) throws IOException {
- return checkNode(jrtPath.getResolvedPath());
+ @Override
+ public SeekableByteChannel truncate(long size)
+ throws IOException {
+ throw new NonWritableChannelException();
}
- private Node checkNode(byte[] path) throws IOException {
- ensureOpen();
- return findNode(path);
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ throw new NonWritableChannelException();
}
- private Node checkResource(AbstractJrtPath jrtPath) throws IOException {
- return checkResource(jrtPath.getResolvedPath());
+ @Override
+ public long size() throws IOException {
+ return size;
}
- private Node checkResource(byte[] path) throws IOException {
- Node node = checkNode(path);
- if (node.isDirectory()) {
- throw new FileSystemException(getString(path) + " is a directory");
+ @Override
+ public void close() throws IOException {
+ rbc.close();
+ }
+ };
}
- assert node.isResource() : "resource node expected here";
- return node;
+ final JrtFileStore getFileStore(JrtPath path) {
+ return new JrtFileStore(path);
}
- private JrtPath toJrtPath(String path) {
- return toJrtPath(getBytes(path));
+ final void ensureOpen() throws IOException {
+ if (!isOpen()) {
+ throw new ClosedFileSystemException();
+ }
}
- private JrtPath toJrtPath(byte[] path) {
- return new JrtPath(this, path);
+ final JrtPath getRootPath() {
+ return rootPath;
}
- private Iterator<Path> nodesToIterator(AbstractJrtPath dir, List<Node> childNodes) {
- Function<Node, Path> nodeToPath =
- child -> dir.resolve(
- toJrtPath(child.getNameString()).getFileName());
- return childNodes.stream().
- map(nodeToPath).collect(toList()).
- iterator();
+ boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException {
+ return checkNode(path1) == checkNode(path2);
}
- private List<Node> rootChildren;
+ boolean isLink(JrtPath path) throws IOException {
+ return checkNode(path).isLink();
+ }
- private synchronized void initRootChildren(AbstractJrtPath jrtPath) throws IOException {
- if (rootChildren == null) {
- rootChildren = new ArrayList<>();
- rootChildren.addAll(findNode(jrtPath).getChildren());
+ boolean exists(JrtPath path) throws IOException {
+ try {
+ checkNode(path);
+ } catch (NoSuchFileException exp) {
+ return false;
}
+ return true;
}
- private Iterator<Path> rootDirIterator(AbstractJrtPath jrtPath) throws IOException {
- initRootChildren(jrtPath);
- return nodesToIterator(jrtPath, rootChildren);
+ boolean isDirectory(JrtPath path, boolean resolveLinks)
+ throws IOException {
+ Node node = checkNode(path);
+ return resolveLinks && node.isLink()
+ ? node.resolveLink(true).isDirectory()
+ : node.isDirectory();
}
- private List<Node> modulesChildren;
-
- private synchronized void initModulesChildren(AbstractJrtPath jrtPath) throws IOException {
- if (modulesChildren == null) {
- modulesChildren = new ArrayList<>();
- modulesChildren.addAll(findNode(jrtPath).getChildren());
+ JrtPath toRealPath(JrtPath path, LinkOption... options)
+ throws IOException {
+ Node node = checkNode(path);
+ if (followLinks(options) && node.isLink()) {
+ node = node.resolveLink();
}
+ // image node holds the real/absolute path name
+ return new JrtPath(this, node.getName(), true);
}
- private Iterator<Path> modulesDirIterator(AbstractJrtPath jrtPath) throws IOException {
- initModulesChildren(jrtPath);
- return nodesToIterator(jrtPath, modulesChildren);
+ private Node lookup(String path) {
+ try {
+ return image.findNode(path);
+ } catch (RuntimeException re) {
+ throw new InvalidPathException(path, re.toString());
+ }
}
- private List<Node> packagesChildren;
-
- private synchronized void initPackagesChildren(AbstractJrtPath jrtPath) throws IOException {
- if (packagesChildren == null) {
- packagesChildren = new ArrayList<>();
- packagesChildren.addAll(findNode(jrtPath).getChildren());
+ private Node lookupSymbolic(String path) {
+ int i = 1;
+ while (i < path.length()) {
+ i = path.indexOf('/', i);
+ if (i == -1) {
+ break;
+ }
+ String prefix = path.substring(0, i);
+ Node node = lookup(prefix);
+ if (node == null) {
+ break;
}
+ if (node.isLink()) {
+ Node link = node.resolveLink(true);
+ // resolved symbolic path concatenated to the rest of the path
+ String resPath = link.getName() + path.substring(i);
+ node = lookup(resPath);
+ return node != null ? node : lookupSymbolic(resPath);
+ }
+ i++;
+ }
+ return null;
}
- private Iterator<Path> packagesDirIterator(AbstractJrtPath jrtPath) throws IOException {
- initPackagesChildren(jrtPath);
- return nodesToIterator(jrtPath, packagesChildren);
+ Node checkNode(JrtPath path) throws IOException {
+ ensureOpen();
+ String p = path.getResolvedPath();
+ Node node = lookup(p);
+ if (node == null) {
+ node = lookupSymbolic(p);
+ if (node == null) {
+ throw new NoSuchFileException(p);
+ }
+ }
+ return node;
}
}