src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java

Print this page

        

*** 22,331 **** * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.internal.jrtfs; import java.io.IOException; ! import java.nio.file.LinkOption; import java.nio.file.FileSystemException; import java.nio.file.InvalidPathException; import java.nio.file.NoSuchFileException; import java.nio.file.NotDirectoryException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; 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 jdk.internal.jimage.ImageReader.Node; ! /** * 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 { ! // System image reader ! private ImageReader bootImage; ! // root path ! private final JrtPath rootPath; private volatile boolean isOpen; ! // 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; } // FileSystem method implementations @Override public boolean isOpen() { return isOpen; } @Override public void close() throws IOException { cleanup(); } @Override protected void finalize() throws Throwable { try { cleanup(); ! } catch (IOException ignored) { } ! super.finalize(); } - // AbstractJrtFileSystem method implementations @Override ! JrtPath getRootPath() { ! return rootPath; } @Override ! boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException { ! ensureOpen(); ! Node node1 = findNode(p1); ! Node node2 = findNode(p2); ! return node1.equals(node2); } @Override ! boolean isLink(AbstractJrtPath jrtPath) throws IOException { ! return checkNode(jrtPath).isLink(); } @Override ! AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException { ! Node node = checkNode(jrtPath); ! if (node.isLink()) { ! node = node.resolveLink(); ! return toJrtPath(getBytes(node.getName())); } ! return jrtPath; } @Override ! JrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options) ! throws IOException { ! Node node = checkNode(jrtPath); ! if (node.isLink() && followLinks(options)) { ! return new JrtFileAttributes(node.resolveLink(true)); } ! return new JrtFileAttributes(node); } @Override ! boolean exists(AbstractJrtPath jrtPath) throws IOException { ! try { ! checkNode(jrtPath); ! } catch (NoSuchFileException exp) { ! return false; } ! return true; } @Override ! boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks) throws IOException { ! Node node = checkNode(jrtPath); ! return resolveLinks && node.isLink() ! ? node.resolveLink(true).isDirectory() ! : node.isDirectory(); } ! @Override ! Iterator<Path> iteratorOf(AbstractJrtPath jrtPath) throws IOException { ! Node node = checkNode(jrtPath).resolveLink(true); if (!node.isDirectory()) { ! throw new NotDirectoryException(getString(jrtPath.getName())); } ! if (node.isRootDir()) { ! return rootDirIterator(jrtPath); ! } else if (node.isModulesDir()) { ! return modulesDirIterator(jrtPath); ! } else if (node.isPackagesDir()) { ! return packagesDirIterator(jrtPath); } ! return nodesToIterator(jrtPath, node.getChildren()); } ! @Override ! byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException { ! final Node node = checkResource(jrtPath); ! return bootImage.getResource(node); } - // Implementation details below this point // clean up this file system - called from finalize and close ! private void cleanup() throws IOException { if (!isOpen) { return; } - synchronized (this) { isOpen = false; ! ! // close all image reader and null out ! bootImage.close(); ! bootImage = 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()); } ! return node; } ! 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; } ! 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); } } } ! return null; } ! private Node findNode(AbstractJrtPath jrtPath) throws IOException { ! return findNode(jrtPath.getResolvedPath()); } ! 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)); } } ! return node; } ! private Node checkNode(AbstractJrtPath jrtPath) throws IOException { ! return checkNode(jrtPath.getResolvedPath()); } ! private Node checkNode(byte[] path) throws IOException { ! ensureOpen(); ! return findNode(path); } ! private Node checkResource(AbstractJrtPath jrtPath) throws IOException { ! return checkResource(jrtPath.getResolvedPath()); } ! private Node checkResource(byte[] path) throws IOException { ! Node node = checkNode(path); ! if (node.isDirectory()) { ! throw new FileSystemException(getString(path) + " is a directory"); } ! assert node.isResource() : "resource node expected here"; ! return node; } ! private JrtPath toJrtPath(String path) { ! return toJrtPath(getBytes(path)); } ! private JrtPath toJrtPath(byte[] path) { ! return new JrtPath(this, path); } ! 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(); } ! private List<Node> rootChildren; ! private synchronized void initRootChildren(AbstractJrtPath jrtPath) throws IOException { ! if (rootChildren == null) { ! rootChildren = new ArrayList<>(); ! rootChildren.addAll(findNode(jrtPath).getChildren()); } } ! private Iterator<Path> rootDirIterator(AbstractJrtPath jrtPath) throws IOException { ! initRootChildren(jrtPath); ! return nodesToIterator(jrtPath, rootChildren); } ! private List<Node> modulesChildren; ! ! private synchronized void initModulesChildren(AbstractJrtPath jrtPath) throws IOException { ! if (modulesChildren == null) { ! modulesChildren = new ArrayList<>(); ! modulesChildren.addAll(findNode(jrtPath).getChildren()); } } ! private Iterator<Path> modulesDirIterator(AbstractJrtPath jrtPath) throws IOException { ! initModulesChildren(jrtPath); ! return nodesToIterator(jrtPath, modulesChildren); } ! private List<Node> packagesChildren; ! ! private synchronized void initPackagesChildren(AbstractJrtPath jrtPath) throws IOException { ! if (packagesChildren == null) { ! packagesChildren = new ArrayList<>(); ! packagesChildren.addAll(findNode(jrtPath).getChildren()); } } ! private Iterator<Path> packagesDirIterator(AbstractJrtPath jrtPath) throws IOException { ! initPackagesChildren(jrtPath); ! return nodesToIterator(jrtPath, packagesChildren); } } --- 22,505 ---- * 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.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.Map; ! 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 FileSystem { ! private final JrtFileSystemProvider provider; ! private final JrtPath rootPath = new JrtPath(this, "/"); private volatile boolean isOpen; + private volatile boolean isClosable; + private SystemImage image; ! 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) {} } ! ! @Override ! public FileSystemProvider provider() { ! return provider; } @Override ! public Iterable<Path> getRootDirectories() { ! ArrayList<Path> dirs = new ArrayList<>(); ! dirs.add(getRootPath()); ! return dirs; } @Override ! 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 ! public final boolean isReadOnly() { ! return true; } @Override ! public final UserPrincipalLookupService getUserPrincipalLookupService() { ! throw new UnsupportedOperationException(); } ! @Override ! public final WatchService newWatchService() { ! throw new UnsupportedOperationException(); } @Override ! public final Iterable<FileStore> getFileStores() { ! ArrayList<FileStore> list = new ArrayList<>(1); ! list.add(getFileStore(getRootPath())); ! return list; } ! ! private static final Set<String> supportedFileAttributeViews ! = Collections.unmodifiableSet( ! new HashSet<String>(Arrays.asList("basic", "jrt"))); ! ! @Override ! public final Set<String> supportedFileAttributeViews() { ! return supportedFileAttributeViews; } @Override ! public final String toString() { ! return "jrt:/"; } ! ! @Override ! public final String getSeparator() { ! return "/"; } @Override ! 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(path); ! if (node.isLink() && followLinks(options)) { ! return new JrtFileAttributes(node.resolveLink(true)); ! } ! return new JrtFileAttributes(node); } ! /** ! * 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(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(); } ! // 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); } ! /////////////// Implementation details below this point ////////// ! ! // static utility methods ! static ReadOnlyFileSystemException readOnly() { ! return new ReadOnlyFileSystemException(); } ! // 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(); ! } } // clean up this file system - called from finalize and close ! void cleanup() throws IOException { if (!isOpen) { return; } synchronized (this) { isOpen = false; ! // close image reader and null out ! image.close(); ! image = null; } } ! // These methods throw read only file system exception ! final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime) ! throws IOException { ! throw readOnly(); } ! ! // These methods throw read only file system exception ! final void createDirectory(JrtPath jrtPath, FileAttribute<?>... attrs) throws IOException { ! throw readOnly(); } ! final void deleteFile(JrtPath jrtPath, boolean failIfNotExists) ! throws IOException { ! throw readOnly(); } ! 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"); } ! final InputStream newInputStream(JrtPath path) throws IOException { ! return new ByteArrayInputStream(getFileContent(path)); } ! 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(); } ! @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 n; } ! @Override ! public SeekableByteChannel truncate(long size) ! throws IOException { ! throw new NonWritableChannelException(); } ! @Override ! public int write(ByteBuffer src) throws IOException { ! throw new NonWritableChannelException(); } ! @Override ! public long size() throws IOException { ! return size; } ! @Override ! public void close() throws IOException { ! rbc.close(); ! } ! }; } ! final JrtFileStore getFileStore(JrtPath path) { ! return new JrtFileStore(path); } ! final void ensureOpen() throws IOException { ! if (!isOpen()) { ! throw new ClosedFileSystemException(); ! } } ! final JrtPath getRootPath() { ! return rootPath; } ! boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException { ! return checkNode(path1) == checkNode(path2); } ! boolean isLink(JrtPath path) throws IOException { ! return checkNode(path).isLink(); ! } ! boolean exists(JrtPath path) throws IOException { ! try { ! checkNode(path); ! } catch (NoSuchFileException exp) { ! return false; } + return true; } ! boolean isDirectory(JrtPath path, boolean resolveLinks) ! throws IOException { ! Node node = checkNode(path); ! return resolveLinks && node.isLink() ! ? node.resolveLink(true).isDirectory() ! : node.isDirectory(); } ! 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 Node lookup(String path) { ! try { ! return image.findNode(path); ! } catch (RuntimeException re) { ! throw new InvalidPathException(path, re.toString()); ! } } ! 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; } ! 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; } }