Path: blob/master/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java
41159 views
/*1* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package jdk.internal.jrtfs;2526import java.io.ByteArrayInputStream;27import java.io.IOException;28import java.io.InputStream;29import java.io.OutputStream;30import java.nio.ByteBuffer;31import java.nio.channels.Channels;32import java.nio.channels.FileChannel;33import java.nio.channels.NonWritableChannelException;34import java.nio.channels.ReadableByteChannel;35import java.nio.channels.SeekableByteChannel;36import java.nio.file.ClosedFileSystemException;37import java.nio.file.CopyOption;38import java.nio.file.DirectoryStream;39import java.nio.file.FileStore;40import java.nio.file.FileSystem;41import java.nio.file.FileSystemException;42import java.nio.file.InvalidPathException;43import java.nio.file.LinkOption;44import java.nio.file.NoSuchFileException;45import java.nio.file.NotDirectoryException;46import java.nio.file.OpenOption;47import java.nio.file.Path;48import java.nio.file.PathMatcher;49import java.nio.file.ReadOnlyFileSystemException;50import java.nio.file.StandardOpenOption;51import java.nio.file.WatchService;52import java.nio.file.attribute.FileAttribute;53import java.nio.file.attribute.FileTime;54import java.nio.file.attribute.UserPrincipalLookupService;55import java.nio.file.spi.FileSystemProvider;56import java.util.ArrayList;57import java.util.Arrays;58import java.util.Collections;59import java.util.HashSet;60import java.util.Iterator;61import java.util.Map;62import java.util.Objects;63import java.util.Set;64import java.util.regex.Pattern;65import jdk.internal.jimage.ImageReader.Node;66import static java.util.stream.Collectors.toList;6768/**69* jrt file system implementation built on System jimage files.70*71* @implNote This class needs to maintain JDK 8 source compatibility.72*73* It is used internally in the JDK to implement jimage/jrtfs access,74* but also compiled and delivered as part of the jrtfs.jar to support access75* to the jimage file provided by the shipped JDK by tools running on JDK 8.76*/77class JrtFileSystem extends FileSystem {7879private final JrtFileSystemProvider provider;80private final JrtPath rootPath = new JrtPath(this, "/");81private volatile boolean isOpen;82private volatile boolean isClosable;83private SystemImage image;8485JrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> env)86throws IOException87{88this.provider = provider;89this.image = SystemImage.open(); // open image file90this.isOpen = true;91this.isClosable = env != null;92}9394// FileSystem method implementations95@Override96public boolean isOpen() {97return isOpen;98}99100@Override101public void close() throws IOException {102if (!isClosable)103throw new UnsupportedOperationException();104cleanup();105}106107@Override108public FileSystemProvider provider() {109return provider;110}111112@Override113public Iterable<Path> getRootDirectories() {114return Collections.singleton(getRootPath());115}116117@Override118public JrtPath getPath(String first, String... more) {119if (more.length == 0) {120return new JrtPath(this, first);121}122StringBuilder sb = new StringBuilder();123sb.append(first);124for (String path : more) {125if (!path.isEmpty()) {126if (sb.length() > 0) {127sb.append('/');128}129sb.append(path);130}131}132return new JrtPath(this, sb.toString());133}134135@Override136public final boolean isReadOnly() {137return true;138}139140@Override141public final UserPrincipalLookupService getUserPrincipalLookupService() {142throw new UnsupportedOperationException();143}144145@Override146public final WatchService newWatchService() {147throw new UnsupportedOperationException();148}149150@Override151public final Iterable<FileStore> getFileStores() {152return Collections.singleton(getFileStore(getRootPath()));153}154155private static final Set<String> supportedFileAttributeViews156= Collections.unmodifiableSet(157new HashSet<String>(Arrays.asList("basic", "jrt")));158159@Override160public final Set<String> supportedFileAttributeViews() {161return supportedFileAttributeViews;162}163164@Override165public final String toString() {166return "jrt:/";167}168169@Override170public final String getSeparator() {171return "/";172}173174@Override175public PathMatcher getPathMatcher(String syntaxAndInput) {176int pos = syntaxAndInput.indexOf(':');177if (pos <= 0 || pos == syntaxAndInput.length()) {178throw new IllegalArgumentException("pos is " + pos);179}180String syntax = syntaxAndInput.substring(0, pos);181String input = syntaxAndInput.substring(pos + 1);182String expr;183if (syntax.equalsIgnoreCase("glob")) {184expr = JrtUtils.toRegexPattern(input);185} else if (syntax.equalsIgnoreCase("regex")) {186expr = input;187} else {188throw new UnsupportedOperationException("Syntax '" + syntax189+ "' not recognized");190}191// return matcher192final Pattern pattern = Pattern.compile(expr);193return (Path path) -> pattern.matcher(path.toString()).matches();194}195196JrtPath resolveLink(JrtPath path) throws IOException {197Node node = checkNode(path);198if (node.isLink()) {199node = node.resolveLink();200return new JrtPath(this, node.getName()); // TBD, normalized?201}202return path;203}204205JrtFileAttributes getFileAttributes(JrtPath path, LinkOption... options)206throws IOException {207Node node = checkNode(path);208if (node.isLink() && followLinks(options)) {209return new JrtFileAttributes(node.resolveLink(true));210}211return new JrtFileAttributes(node);212}213214/**215* returns the list of child paths of the given directory "path"216*217* @param path name of the directory whose content is listed218* @return iterator for child paths of the given directory path219*/220Iterator<Path> iteratorOf(JrtPath path, DirectoryStream.Filter<? super Path> filter)221throws IOException {222Node node = checkNode(path).resolveLink(true);223if (!node.isDirectory()) {224throw new NotDirectoryException(path.getName());225}226if (filter == null) {227return node.getChildren()228.stream()229.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))230.iterator();231}232return node.getChildren()233.stream()234.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))235.filter(p -> { try { return filter.accept(p);236} catch (IOException x) {}237return false;238})239.iterator();240}241242// returns the content of the file resource specified by the path243byte[] getFileContent(JrtPath path) throws IOException {244Node node = checkNode(path);245if (node.isDirectory()) {246throw new FileSystemException(path + " is a directory");247}248//assert node.isResource() : "resource node expected here";249return image.getResource(node);250}251252/////////////// Implementation details below this point //////////253254// static utility methods255static ReadOnlyFileSystemException readOnly() {256return new ReadOnlyFileSystemException();257}258259// do the supplied options imply that we have to chase symlinks?260static boolean followLinks(LinkOption... options) {261if (options != null) {262for (LinkOption lo : options) {263Objects.requireNonNull(lo);264if (lo == LinkOption.NOFOLLOW_LINKS) {265return false;266} else {267throw new AssertionError("should not reach here");268}269}270}271return true;272}273274// check that the options passed are supported by (read-only) jrt file system275static void checkOptions(Set<? extends OpenOption> options) {276// check for options of null type and option is an intance of StandardOpenOption277for (OpenOption option : options) {278Objects.requireNonNull(option);279if (!(option instanceof StandardOpenOption)) {280throw new IllegalArgumentException(281"option class: " + option.getClass());282}283}284if (options.contains(StandardOpenOption.WRITE) ||285options.contains(StandardOpenOption.APPEND)) {286throw readOnly();287}288}289290// clean up this file system - called from finalize and close291synchronized void cleanup() throws IOException {292if (isOpen) {293isOpen = false;294image.close();295image = null;296}297}298299// These methods throw read only file system exception300final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime)301throws IOException {302throw readOnly();303}304305// These methods throw read only file system exception306final void createDirectory(JrtPath jrtPath, FileAttribute<?>... attrs) throws IOException {307throw readOnly();308}309310final void deleteFile(JrtPath jrtPath, boolean failIfNotExists)311throws IOException {312throw readOnly();313}314315final OutputStream newOutputStream(JrtPath jrtPath, OpenOption... options)316throws IOException {317throw readOnly();318}319320final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOption... options)321throws IOException {322throw readOnly();323}324325final FileChannel newFileChannel(JrtPath path,326Set<? extends OpenOption> options,327FileAttribute<?>... attrs)328throws IOException {329throw new UnsupportedOperationException("newFileChannel");330}331332final InputStream newInputStream(JrtPath path) throws IOException {333return new ByteArrayInputStream(getFileContent(path));334}335336final SeekableByteChannel newByteChannel(JrtPath path,337Set<? extends OpenOption> options,338FileAttribute<?>... attrs)339throws IOException {340checkOptions(options);341342byte[] buf = getFileContent(path);343final ReadableByteChannel rbc344= Channels.newChannel(new ByteArrayInputStream(buf));345final long size = buf.length;346return new SeekableByteChannel() {347long read = 0;348349@Override350public boolean isOpen() {351return rbc.isOpen();352}353354@Override355public long position() throws IOException {356return read;357}358359@Override360public SeekableByteChannel position(long pos)361throws IOException {362throw new UnsupportedOperationException();363}364365@Override366public int read(ByteBuffer dst) throws IOException {367int n = rbc.read(dst);368if (n > 0) {369read += n;370}371return n;372}373374@Override375public SeekableByteChannel truncate(long size)376throws IOException {377throw new NonWritableChannelException();378}379380@Override381public int write(ByteBuffer src) throws IOException {382throw new NonWritableChannelException();383}384385@Override386public long size() throws IOException {387return size;388}389390@Override391public void close() throws IOException {392rbc.close();393}394};395}396397final JrtFileStore getFileStore(JrtPath path) {398return new JrtFileStore(path);399}400401final void ensureOpen() throws IOException {402if (!isOpen()) {403throw new ClosedFileSystemException();404}405}406407final JrtPath getRootPath() {408return rootPath;409}410411boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException {412return checkNode(path1) == checkNode(path2);413}414415boolean isLink(JrtPath path) throws IOException {416return checkNode(path).isLink();417}418419boolean exists(JrtPath path) throws IOException {420try {421checkNode(path);422} catch (NoSuchFileException exp) {423return false;424}425return true;426}427428boolean isDirectory(JrtPath path, boolean resolveLinks)429throws IOException {430Node node = checkNode(path);431return resolveLinks && node.isLink()432? node.resolveLink(true).isDirectory()433: node.isDirectory();434}435436JrtPath toRealPath(JrtPath path, LinkOption... options)437throws IOException {438Node node = checkNode(path);439if (followLinks(options) && node.isLink()) {440node = node.resolveLink();441}442// image node holds the real/absolute path name443return new JrtPath(this, node.getName(), true);444}445446private Node lookup(String path) {447try {448return image.findNode(path);449} catch (RuntimeException | IOException ex) {450throw new InvalidPathException(path, ex.toString());451}452}453454private Node lookupSymbolic(String path) {455int i = 1;456while (i < path.length()) {457i = path.indexOf('/', i);458if (i == -1) {459break;460}461String prefix = path.substring(0, i);462Node node = lookup(prefix);463if (node == null) {464break;465}466if (node.isLink()) {467Node link = node.resolveLink(true);468// resolved symbolic path concatenated to the rest of the path469String resPath = link.getName() + path.substring(i);470node = lookup(resPath);471return node != null ? node : lookupSymbolic(resPath);472}473i++;474}475return null;476}477478Node checkNode(JrtPath path) throws IOException {479ensureOpen();480String p = path.getResolvedPath();481Node node = lookup(p);482if (node == null) {483node = lookupSymbolic(p);484if (node == null) {485throw new NoSuchFileException(p);486}487}488return node;489}490}491492493