changeset 13548:958c08e86370

8066860: Enhance the jrt file system to support exploded builds Reviewed-by: chegar
author sundar
date Thu, 30 Jul 2015 23:42:31 +0530
parents 9d56b977df96
children 2f4a16106ae7
files src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileAttributes.java src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileSystem.java src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtPath.java src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileAttributes.java src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileSystem.java src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedPath.java src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java test/jdk/internal/jrtfs/Basic.java
diffstat 15 files changed, 2395 insertions(+), 1432 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileAttributes.java	Thu Jul 30 23:42:31 2015 +0530
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jrtfs;
+
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Formatter;
+
+/**
+ * Base class for file attributes supported by jrt file systems.
+ */
+public abstract class AbstractJrtFileAttributes implements BasicFileAttributes {
+
+    // jrt fs specific attributes
+    /**
+     * Compressed resource file. If not available or not applicable, 0L is
+     * returned.
+     *
+     * @return the compressed resource size for compressed resources.
+     */
+    public abstract long compressedSize();
+
+    /**
+     * "file" extension of a file resource.
+     *
+     * @return extension string for the file resource
+     */
+    public abstract String extension();
+
+    @Override
+    public final String toString() {
+        StringBuilder sb = new StringBuilder(1024);
+        try (Formatter fm = new Formatter(sb)) {
+            if (creationTime() != null) {
+                fm.format("    creationTime    : %tc%n", creationTime().toMillis());
+            } else {
+                fm.format("    creationTime    : null%n");
+            }
+
+            if (lastAccessTime() != null) {
+                fm.format("    lastAccessTime  : %tc%n", lastAccessTime().toMillis());
+            } else {
+                fm.format("    lastAccessTime  : null%n");
+            }
+            fm.format("    lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
+            fm.format("    isRegularFile   : %b%n", isRegularFile());
+            fm.format("    isDirectory     : %b%n", isDirectory());
+            fm.format("    isSymbolicLink  : %b%n", isSymbolicLink());
+            fm.format("    isOther         : %b%n", isOther());
+            fm.format("    fileKey         : %s%n", fileKey());
+            fm.format("    size            : %d%n", size());
+            fm.format("    compressedSize  : %d%n", compressedSize());
+            fm.format("    extension       : %s%n", extension());
+        }
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileSystem.java	Thu Jul 30 23:42:31 2015 +0530
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * 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.charset.Charset;
+import java.nio.file.ClosedFileSystemException;
+import java.nio.file.CopyOption;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+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.Set;
+import java.util.regex.Pattern;
+
+/**
+ * Base class for jrt file systems. jrt filesystem implementations are currently
+ * available on top of .jimage file and on top "exploded" build directories.
+ */
+abstract class AbstractJrtFileSystem extends FileSystem {
+
+    private final JrtFileSystemProvider provider;
+
+    AbstractJrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> options) {
+        this.provider = provider;
+    }
+
+    private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+    // static utility methods
+    static ReadOnlyFileSystemException readOnly() {
+        return new ReadOnlyFileSystemException();
+    }
+
+    // if a Path does not exist, throw exception
+    static void checkExists(Path path) {
+        if (Files.notExists(path)) {
+            throw new FileSystemNotFoundException(path.toString());
+        }
+    }
+
+    static byte[] getBytes(String name) {
+        return name.getBytes(UTF_8);
+    }
+
+    static String getString(byte[] name) {
+        return new String(name, UTF_8);
+    }
+
+    // do the supplied options imply that we have to chase symlinks?
+    static boolean followLinks(LinkOption... options) {
+        if (options != null) {
+            for (LinkOption lo : options) {
+                if (lo == LinkOption.NOFOLLOW_LINKS) {
+                    return false;
+                } else if (lo == null) {
+                    throw new NullPointerException();
+                } 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) {
+            if (option == null) {
+                throw new NullPointerException();
+            }
+            if (!(option instanceof StandardOpenOption)) {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        if (options.contains(StandardOpenOption.WRITE)
+                || options.contains(StandardOpenOption.APPEND)) {
+            throw readOnly();
+        }
+    }
+
+    // FileSystem method implementations
+    @Override
+    public FileSystemProvider provider() {
+        return provider;
+    }
+
+    @Override
+    public Iterable<Path> getRootDirectories() {
+        ArrayList<Path> pathArr = new ArrayList<>();
+        pathArr.add(getRootPath());
+        return pathArr;
+    }
+
+    @Override
+    public AbstractJrtPath getPath(String first, String... more) {
+        String path;
+        if (more.length == 0) {
+            path = first;
+        } else {
+            StringBuilder sb = new StringBuilder();
+            sb.append(first);
+            for (String segment : more) {
+                if (segment.length() > 0) {
+                    if (sb.length() > 0) {
+                        sb.append('/');
+                    }
+                    sb.append(segment);
+                }
+            }
+            path = sb.toString();
+        }
+        return getRootPath().newJrtPath(getBytes(path));
+    }
+
+    @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 "/";
+    }
+
+    private static final String GLOB_SYNTAX = "glob";
+    private static final String REGEX_SYNTAX = "regex";
+
+    @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_SYNTAX)) {
+            expr = JrtUtils.toRegexPattern(input);
+        } else {
+            if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) {
+                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();
+    }
+
+    // These methods throw read only file system exception
+    final void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime)
+            throws IOException {
+        throw readOnly();
+    }
+
+    final void createDirectory(byte[] path, FileAttribute<?>... attrs) throws IOException {
+        throw readOnly();
+    }
+
+    final void deleteFile(byte[] path, boolean failIfNotExists)
+            throws IOException {
+        throw readOnly();
+    }
+
+    final OutputStream newOutputStream(byte[] path, OpenOption... options)
+            throws IOException {
+        throw readOnly();
+    }
+
+    final void copyFile(boolean deletesrc, byte[] src, byte[] dst, CopyOption... options)
+            throws IOException {
+        throw readOnly();
+    }
+
+    final FileChannel newFileChannel(byte[] path,
+            Set<? extends OpenOption> options,
+            FileAttribute<?>... attrs)
+            throws IOException {
+        throw new UnsupportedOperationException("newFileChannel");
+    }
+
+    final InputStream newInputStream(byte[] path) throws IOException {
+        return new ByteArrayInputStream(getFileContent(path));
+    }
+
+    final SeekableByteChannel newByteChannel(byte[] 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(AbstractJrtPath path) {
+        return new JrtFileStore(path);
+    }
+
+    final void ensureOpen() throws IOException {
+        if (!isOpen()) {
+            throw new ClosedFileSystemException();
+        }
+    }
+
+    // abstract methods to be implemented by a particular jrt file system
+    abstract AbstractJrtPath getRootPath();
+
+    abstract boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException;
+
+    abstract boolean isLink(AbstractJrtPath jrtPath) throws IOException;
+
+    abstract AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException;
+
+    abstract AbstractJrtFileAttributes getFileAttributes(byte[] path, LinkOption... options) throws IOException;
+
+    abstract boolean exists(byte[] path) throws IOException;
+
+    abstract boolean isDirectory(byte[] path, boolean resolveLinks) throws IOException;
+
+    /**
+     * returns the list of child paths of the given directory "path"
+     *
+     * @param path name of the directory whose content is listed
+     * @param childPrefix prefix added to returned children names - may be null
+     * in which case absolute child paths are returned
+     * @return iterator for child paths of the given directory path
+     */
+    abstract Iterator<Path> iteratorOf(byte[] path, String childPrefix)
+            throws IOException;
+
+    // returns the content of the file resource specified by the path
+    abstract byte[] getFileContent(byte[] path) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtPath.java	Thu Jul 30 23:42:31 2015 +0530
@@ -0,0 +1,929 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jrtfs;
+
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.channels.*;
+import java.nio.file.*;
+import java.nio.file.DirectoryStream.Filter;
+import java.nio.file.attribute.*;
+import java.util.*;
+import static java.nio.file.StandardOpenOption.*;
+import static java.nio.file.StandardCopyOption.*;
+
+/**
+ * Base class for Path implementation of jrt file systems.
+ */
+abstract class AbstractJrtPath implements Path {
+
+    protected final AbstractJrtFileSystem jrtfs;
+    private final byte[] path;
+    private volatile int[] offsets;
+    private int hashcode = 0;  // cached hashcode (created lazily)
+
+    AbstractJrtPath(AbstractJrtFileSystem jrtfs, byte[] path) {
+        this(jrtfs, path, false);
+        this.resolved = null;
+    }
+
+    AbstractJrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
+        this.resolved = null;
+        this.jrtfs = jrtfs;
+        if (normalized) {
+            this.path = path;
+        } else {
+            this.path = normalize(path);
+        }
+    }
+
+    // factory methods to create subtypes of AbstractJrtPath
+    protected abstract AbstractJrtPath newJrtPath(byte[] path);
+
+    protected abstract AbstractJrtPath newJrtPath(byte[] path, boolean normalized);
+
+    final byte[] getName() {
+        return path;
+    }
+
+    @Override
+    public final AbstractJrtPath getRoot() {
+        if (this.isAbsolute()) {
+            return jrtfs.getRootPath();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public final AbstractJrtPath getFileName() {
+        initOffsets();
+        int count = offsets.length;
+        if (count == 0) {
+            return null;  // no elements so no name
+        }
+        if (count == 1 && path[0] != '/') {
+            return this;
+        }
+        int lastOffset = offsets[count - 1];
+        int len = path.length - lastOffset;
+        byte[] result = new byte[len];
+        System.arraycopy(path, lastOffset, result, 0, len);
+        return newJrtPath(result);
+    }
+
+    @Override
+    public final AbstractJrtPath getParent() {
+        initOffsets();
+        int count = offsets.length;
+        if (count == 0) // no elements so no parent
+        {
+            return null;
+        }
+        int len = offsets[count - 1] - 1;
+        if (len <= 0) // parent is root only (may be null)
+        {
+            return getRoot();
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(path, 0, result, 0, len);
+        return newJrtPath(result);
+    }
+
+    @Override
+    public final int getNameCount() {
+        initOffsets();
+        return offsets.length;
+    }
+
+    @Override
+    public final AbstractJrtPath getName(int index) {
+        initOffsets();
+        if (index < 0 || index >= offsets.length) {
+            throw new IllegalArgumentException();
+        }
+        int begin = offsets[index];
+        int len;
+        if (index == (offsets.length - 1)) {
+            len = path.length - begin;
+        } else {
+            len = offsets[index + 1] - begin - 1;
+        }
+        // construct result
+        byte[] result = new byte[len];
+        System.arraycopy(path, begin, result, 0, len);
+        return newJrtPath(result);
+    }
+
+    @Override
+    public final AbstractJrtPath subpath(int beginIndex, int endIndex) {
+        initOffsets();
+        if (beginIndex < 0
+                || beginIndex >= offsets.length
+                || endIndex > offsets.length
+                || beginIndex >= endIndex) {
+            throw new IllegalArgumentException();
+        }
+
+        // starting offset and length
+        int begin = offsets[beginIndex];
+        int len;
+        if (endIndex == offsets.length) {
+            len = path.length - begin;
+        } else {
+            len = offsets[endIndex] - begin - 1;
+        }
+        // construct result
+        byte[] result = new byte[len];
+        System.arraycopy(path, begin, result, 0, len);
+        return newJrtPath(result);
+    }
+
+    @Override
+    public final AbstractJrtPath toRealPath(LinkOption... options) throws IOException {
+        AbstractJrtPath realPath = newJrtPath(getResolvedPath()).toAbsolutePath();
+        realPath = JrtFileSystem.followLinks(options) ? jrtfs.resolveLink(this) : realPath;
+        realPath.checkAccess();
+        return realPath;
+    }
+
+    final AbstractJrtPath readSymbolicLink() throws IOException {
+        if (!jrtfs.isLink(this)) {
+            throw new IOException("not a symbolic link");
+        }
+
+        return jrtfs.resolveLink(this);
+    }
+
+    final boolean isHidden() {
+        return false;
+    }
+
+    @Override
+    public final AbstractJrtPath toAbsolutePath() {
+        if (isAbsolute()) {
+            return this;
+        } else {
+            //add / bofore the existing path
+            byte[] tmp = new byte[path.length + 1];
+            tmp[0] = '/';
+            System.arraycopy(path, 0, tmp, 1, path.length);
+            return newJrtPath(tmp).normalize();
+        }
+    }
+
+    @Override
+    public final URI toUri() {
+        try {
+            return new URI("jrt",
+                    JrtFileSystem.getString(toAbsolutePath().path),
+                    null);
+        } catch (URISyntaxException ex) {
+            throw new AssertionError(ex);
+        }
+    }
+
+    private boolean equalsNameAt(AbstractJrtPath other, int index) {
+        int mbegin = offsets[index];
+        int mlen;
+        if (index == (offsets.length - 1)) {
+            mlen = path.length - mbegin;
+        } else {
+            mlen = offsets[index + 1] - mbegin - 1;
+        }
+        int obegin = other.offsets[index];
+        int olen;
+        if (index == (other.offsets.length - 1)) {
+            olen = other.path.length - obegin;
+        } else {
+            olen = other.offsets[index + 1] - obegin - 1;
+        }
+        if (mlen != olen) {
+            return false;
+        }
+        int n = 0;
+        while (n < mlen) {
+            if (path[mbegin + n] != other.path[obegin + n]) {
+                return false;
+            }
+            n++;
+        }
+        return true;
+    }
+
+    @Override
+    public final AbstractJrtPath relativize(Path other) {
+        final AbstractJrtPath o = checkPath(other);
+        if (o.equals(this)) {
+            return newJrtPath(new byte[0], true);
+        }
+        if (/* this.getFileSystem() != o.getFileSystem() || */this.isAbsolute() != o.isAbsolute()) {
+            throw new IllegalArgumentException();
+        }
+        int mc = this.getNameCount();
+        int oc = o.getNameCount();
+        int n = Math.min(mc, oc);
+        int i = 0;
+        while (i < n) {
+            if (!equalsNameAt(o, i)) {
+                break;
+            }
+            i++;
+        }
+        int dotdots = mc - i;
+        int len = dotdots * 3 - 1;
+        if (i < oc) {
+            len += (o.path.length - o.offsets[i] + 1);
+        }
+        byte[] result = new byte[len];
+
+        int pos = 0;
+        while (dotdots > 0) {
+            result[pos++] = (byte) '.';
+            result[pos++] = (byte) '.';
+            if (pos < len) // no tailing slash at the end
+            {
+                result[pos++] = (byte) '/';
+            }
+            dotdots--;
+        }
+        if (i < oc) {
+            System.arraycopy(o.path, o.offsets[i],
+                    result, pos,
+                    o.path.length - o.offsets[i]);
+        }
+        return newJrtPath(result);
+    }
+
+    @Override
+    public AbstractJrtFileSystem getFileSystem() {
+        return jrtfs;
+    }
+
+    @Override
+    public final boolean isAbsolute() {
+        return (this.path.length > 0 && path[0] == '/');
+    }
+
+    @Override
+    public final AbstractJrtPath resolve(Path other) {
+        final AbstractJrtPath o = checkPath(other);
+        if (o.isAbsolute()) {
+            return o;
+        }
+        byte[] res;
+        if (this.path[path.length - 1] == '/') {
+            res = new byte[path.length + o.path.length];
+            System.arraycopy(path, 0, res, 0, path.length);
+            System.arraycopy(o.path, 0, res, path.length, o.path.length);
+        } else {
+            res = new byte[path.length + 1 + o.path.length];
+            System.arraycopy(path, 0, res, 0, path.length);
+            res[path.length] = '/';
+            System.arraycopy(o.path, 0, res, path.length + 1, o.path.length);
+        }
+        return newJrtPath(res);
+    }
+
+    @Override
+    public final Path resolveSibling(Path other) {
+        if (other == null) {
+            throw new NullPointerException();
+        }
+        Path parent = getParent();
+        return (parent == null) ? other : parent.resolve(other);
+    }
+
+    @Override
+    public final boolean startsWith(Path other) {
+        final AbstractJrtPath o = checkPath(other);
+        if (o.isAbsolute() != this.isAbsolute()
+                || o.path.length > this.path.length) {
+            return false;
+        }
+        int olast = o.path.length;
+        for (int i = 0; i < olast; i++) {
+            if (o.path[i] != this.path[i]) {
+                return false;
+            }
+        }
+        olast--;
+        return o.path.length == this.path.length
+                || o.path[olast] == '/'
+                || this.path[olast + 1] == '/';
+    }
+
+    @Override
+    public final boolean endsWith(Path other) {
+        final AbstractJrtPath o = checkPath(other);
+        int olast = o.path.length - 1;
+        if (olast > 0 && o.path[olast] == '/') {
+            olast--;
+        }
+        int last = this.path.length - 1;
+        if (last > 0 && this.path[last] == '/') {
+            last--;
+        }
+        if (olast == -1) // o.path.length == 0
+        {
+            return last == -1;
+        }
+        if ((o.isAbsolute() && (!this.isAbsolute() || olast != last))
+                || (last < olast)) {
+            return false;
+        }
+        for (; olast >= 0; olast--, last--) {
+            if (o.path[olast] != this.path[last]) {
+                return false;
+            }
+        }
+        return o.path[olast + 1] == '/'
+                || last == -1 || this.path[last] == '/';
+    }
+
+    @Override
+    public final AbstractJrtPath resolve(String other) {
+        return resolve(getFileSystem().getPath(other));
+    }
+
+    @Override
+    public final Path resolveSibling(String other) {
+        return resolveSibling(getFileSystem().getPath(other));
+    }
+
+    @Override
+    public final boolean startsWith(String other) {
+        return startsWith(getFileSystem().getPath(other));
+    }
+
+    @Override
+    public final boolean endsWith(String other) {
+        return endsWith(getFileSystem().getPath(other));
+    }
+
+    @Override
+    public final AbstractJrtPath normalize() {
+        byte[] res = getResolved();
+        if (res == path) // no change
+        {
+            return this;
+        }
+        return newJrtPath(res, true);
+    }
+
+    private AbstractJrtPath checkPath(Path path) {
+        if (path == null) {
+            throw new NullPointerException();
+        }
+        if (!(path instanceof AbstractJrtPath)) {
+            throw new ProviderMismatchException();
+        }
+        return (AbstractJrtPath) path;
+    }
+
+    // create offset list if not already created
+    private void initOffsets() {
+        if (offsets == null) {
+            int count, index;
+            // count names
+            count = 0;
+            index = 0;
+            while (index < path.length) {
+                byte c = path[index++];
+                if (c != '/') {
+                    count++;
+                    while (index < path.length && path[index] != '/') {
+                        index++;
+                    }
+                }
+            }
+            // populate offsets
+            int[] result = new int[count];
+            count = 0;
+            index = 0;
+            while (index < path.length) {
+                byte c = path[index];
+                if (c == '/') {
+                    index++;
+                } else {
+                    result[count++] = index++;
+                    while (index < path.length && path[index] != '/') {
+                        index++;
+                    }
+                }
+            }
+            synchronized (this) {
+                if (offsets == null) {
+                    offsets = result;
+                }
+            }
+        }
+    }
+
+    private volatile byte[] resolved;
+
+    final byte[] getResolvedPath() {
+        byte[] r = resolved;
+        if (r == null) {
+            if (isAbsolute()) {
+                r = getResolved();
+            } else {
+                r = toAbsolutePath().getResolvedPath();
+            }
+            resolved = r;
+        }
+        return resolved;
+    }
+
+    // removes redundant slashs, replace "\" to separator "/"
+    // and check for invalid characters
+    private static byte[] normalize(byte[] path) {
+        if (path.length == 0) {
+            return path;
+        }
+        byte prevC = 0;
+        for (int i = 0; i < path.length; i++) {
+            byte c = path[i];
+            if (c == '\\') {
+                return normalize(path, i);
+            }
+            if (c == (byte) '/' && prevC == '/') {
+                return normalize(path, i - 1);
+            }
+            if (c == '\u0000') {
+                throw new InvalidPathException(JrtFileSystem.getString(path),
+                        "Path: nul character not allowed");
+            }
+            prevC = c;
+        }
+
+        if (path.length > 1 && path[path.length - 1] == '/') {
+            return Arrays.copyOf(path, path.length - 1);
+        }
+
+        return path;
+    }
+
+    private static byte[] normalize(byte[] path, int off) {
+        byte[] to = new byte[path.length];
+        int n = 0;
+        while (n < off) {
+            to[n] = path[n];
+            n++;
+        }
+        int m = n;
+        byte prevC = 0;
+        while (n < path.length) {
+            byte c = path[n++];
+            if (c == (byte) '\\') {
+                c = (byte) '/';
+            }
+            if (c == (byte) '/' && prevC == (byte) '/') {
+                continue;
+            }
+            if (c == '\u0000') {
+                throw new InvalidPathException(JrtFileSystem.getString(path),
+                        "Path: nul character not allowed");
+            }
+            to[m++] = c;
+            prevC = c;
+        }
+        if (m > 1 && to[m - 1] == '/') {
+            m--;
+        }
+        return (m == to.length) ? to : Arrays.copyOf(to, m);
+    }
+
+    // Remove DotSlash(./) and resolve DotDot (..) components
+    private byte[] getResolved() {
+        if (path.length == 0) {
+            return path;
+        }
+        for (int i = 0; i < path.length; i++) {
+            byte c = path[i];
+            if (c == (byte) '.') {
+                return resolve0();
+            }
+        }
+
+        return path;
+    }
+
+    // TBD: performance, avoid initOffsets
+    private byte[] resolve0() {
+        byte[] to = new byte[path.length];
+        int nc = getNameCount();
+        int[] lastM = new int[nc];
+        int lastMOff = -1;
+        int m = 0;
+        for (int i = 0; i < nc; i++) {
+            int n = offsets[i];
+            int len = (i == offsets.length - 1)
+                    ? (path.length - n) : (offsets[i + 1] - n - 1);
+            if (len == 1 && path[n] == (byte) '.') {
+                if (m == 0 && path[0] == '/') // absolute path
+                {
+                    to[m++] = '/';
+                }
+                continue;
+            }
+            if (len == 2 && path[n] == '.' && path[n + 1] == '.') {
+                if (lastMOff >= 0) {
+                    m = lastM[lastMOff--];  // retreat
+                    continue;
+                }
+                if (path[0] == '/') {  // "/../xyz" skip
+                    if (m == 0) {
+                        to[m++] = '/';
+                    }
+                } else {               // "../xyz" -> "../xyz"
+                    if (m != 0 && to[m - 1] != '/') {
+                        to[m++] = '/';
+                    }
+                    while (len-- > 0) {
+                        to[m++] = path[n++];
+                    }
+                }
+                continue;
+            }
+            if (m == 0 && path[0] == '/' || // absolute path
+                    m != 0 && to[m - 1] != '/') {   // not the first name
+                to[m++] = '/';
+            }
+            lastM[++lastMOff] = m;
+            while (len-- > 0) {
+                to[m++] = path[n++];
+            }
+        }
+        if (m > 1 && to[m - 1] == '/') {
+            m--;
+        }
+        return (m == to.length) ? to : Arrays.copyOf(to, m);
+    }
+
+    @Override
+    public final String toString() {
+        return JrtFileSystem.getString(path);
+    }
+
+    @Override
+    public final int hashCode() {
+        int h = hashcode;
+        if (h == 0) {
+            hashcode = h = Arrays.hashCode(path);
+        }
+        return h;
+    }
+
+    @Override
+    public final boolean equals(Object obj) {
+        return obj != null
+                && obj instanceof AbstractJrtPath
+                && this.jrtfs == ((AbstractJrtPath) obj).jrtfs
+                && compareTo((Path) obj) == 0;
+    }
+
+    @Override
+    public final int compareTo(Path other) {
+        final AbstractJrtPath o = checkPath(other);
+        int len1 = this.path.length;
+        int len2 = o.path.length;
+
+        int n = Math.min(len1, len2);
+        byte v1[] = this.path;
+        byte v2[] = o.path;
+
+        int k = 0;
+        while (k < n) {
+            int c1 = v1[k] & 0xff;
+            int c2 = v2[k] & 0xff;
+            if (c1 != c2) {
+                return c1 - c2;
+            }
+            k++;
+        }
+        return len1 - len2;
+    }
+
+    @Override
+    public final WatchKey register(
+            WatchService watcher,
+            WatchEvent.Kind<?>[] events,
+            WatchEvent.Modifier... modifiers) {
+        if (watcher == null || events == null || modifiers == null) {
+            throw new NullPointerException();
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
+        return register(watcher, events, new WatchEvent.Modifier[0]);
+    }
+
+    @Override
+    public final File toFile() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final Iterator<Path> iterator() {
+        return new Iterator<Path>() {
+            private int i = 0;
+
+            @Override
+            public boolean hasNext() {
+                return (i < getNameCount());
+            }
+
+            @Override
+            public Path next() {
+                if (i < getNameCount()) {
+                    Path result = getName(i);
+                    i++;
+                    return result;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+
+            @Override
+            public void remove() {
+                throw new ReadOnlyFileSystemException();
+            }
+        };
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    // Helpers for JrtFileSystemProvider and JrtFileSystem
+    final int getPathLength() {
+        return path.length;
+    }
+
+    final void createDirectory(FileAttribute<?>... attrs)
+            throws IOException {
+        jrtfs.createDirectory(getResolvedPath(), attrs);
+    }
+
+    final InputStream newInputStream(OpenOption... options) throws IOException {
+        if (options.length > 0) {
+            for (OpenOption opt : options) {
+                if (opt != READ) {
+                    throw new UnsupportedOperationException("'" + opt + "' not allowed");
+                }
+            }
+        }
+        return jrtfs.newInputStream(getResolvedPath());
+    }
+
+    final DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
+            throws IOException {
+        return new JrtDirectoryStream(this, filter);
+    }
+
+    final void delete() throws IOException {
+        jrtfs.deleteFile(getResolvedPath(), true);
+    }
+
+    final void deleteIfExists() throws IOException {
+        jrtfs.deleteFile(getResolvedPath(), false);
+    }
+
+    final AbstractJrtFileAttributes getAttributes(LinkOption... options) throws IOException {
+        AbstractJrtFileAttributes zfas = jrtfs.getFileAttributes(getResolvedPath(), options);
+        if (zfas == null) {
+            throw new NoSuchFileException(toString());
+        }
+        return zfas;
+    }
+
+    final void setAttribute(String attribute, Object value, LinkOption... options)
+            throws IOException {
+        String type;
+        String attr;
+        int colonPos = attribute.indexOf(':');
+        if (colonPos == -1) {
+            type = "basic";
+            attr = attribute;
+        } else {
+            type = attribute.substring(0, colonPos++);
+            attr = attribute.substring(colonPos);
+        }
+        JrtFileAttributeView view = JrtFileAttributeView.get(this, type, options);
+        if (view == null) {
+            throw new UnsupportedOperationException("view <" + view + "> is not supported");
+        }
+        view.setAttribute(attr, value);
+    }
+
+    final void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
+            throws IOException {
+        jrtfs.setTimes(getResolvedPath(), mtime, atime, ctime);
+    }
+
+    final Map<String, Object> readAttributes(String attributes, LinkOption... options)
+            throws IOException {
+        String view;
+        String attrs;
+        int colonPos = attributes.indexOf(':');
+        if (colonPos == -1) {
+            view = "basic";
+            attrs = attributes;
+        } else {
+            view = attributes.substring(0, colonPos++);
+            attrs = attributes.substring(colonPos);
+        }
+        JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view, options);
+        if (jrtfv == null) {
+            throw new UnsupportedOperationException("view not supported");
+        }
+        return jrtfv.readAttributes(attrs);
+    }
+
+    final FileStore getFileStore() throws IOException {
+        // each JrtFileSystem only has one root (as requested for now)
+        if (exists()) {
+            return jrtfs.getFileStore(this);
+        }
+        throw new NoSuchFileException(JrtFileSystem.getString(path));
+    }
+
+    final boolean isSameFile(Path other) throws IOException {
+        if (this.equals(other)) {
+            return true;
+        }
+        if (other == null
+                || this.getFileSystem() != other.getFileSystem()) {
+            return false;
+        }
+        this.checkAccess();
+        AbstractJrtPath target = (AbstractJrtPath) other;
+        target.checkAccess();
+        return Arrays.equals(this.getResolvedPath(), target.getResolvedPath())
+                || jrtfs.isSameFile(this, target);
+    }
+
+    final SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
+            FileAttribute<?>... attrs)
+            throws IOException {
+        return jrtfs.newByteChannel(getResolvedPath(), options, attrs);
+    }
+
+    final FileChannel newFileChannel(Set<? extends OpenOption> options,
+            FileAttribute<?>... attrs)
+            throws IOException {
+        return jrtfs.newFileChannel(getResolvedPath(), options, attrs);
+    }
+
+    final void checkAccess(AccessMode... modes) throws IOException {
+        boolean w = false;
+        boolean x = false;
+        for (AccessMode mode : modes) {
+            switch (mode) {
+                case READ:
+                    break;
+                case WRITE:
+                    w = true;
+                    break;
+                case EXECUTE:
+                    x = true;
+                    break;
+                default:
+                    throw new UnsupportedOperationException();
+            }
+        }
+
+        BasicFileAttributes attrs = jrtfs.getFileAttributes(getResolvedPath());
+        if (attrs == null && (path.length != 1 || path[0] != '/')) {
+            throw new NoSuchFileException(toString());
+        }
+        if (w) {
+//            if (jrtfs.isReadOnly())
+            throw new AccessDeniedException(toString());
+        }
+        if (x) {
+            throw new AccessDeniedException(toString());
+        }
+    }
+
+    final boolean exists() {
+        try {
+            return jrtfs.exists(getResolvedPath());
+        } catch (IOException x) {
+        }
+        return false;
+    }
+
+    final OutputStream newOutputStream(OpenOption... options) throws IOException {
+        if (options.length == 0) {
+            return jrtfs.newOutputStream(getResolvedPath(),
+                    CREATE_NEW, WRITE);
+        }
+        return jrtfs.newOutputStream(getResolvedPath(), options);
+    }
+
+    final void move(AbstractJrtPath target, CopyOption... options)
+            throws IOException {
+        if (this.jrtfs == target.jrtfs) {
+            jrtfs.copyFile(true,
+                    getResolvedPath(), target.getResolvedPath(),
+                    options);
+        } else {
+            copyToTarget(target, options);
+            delete();
+        }
+    }
+
+    final void copy(AbstractJrtPath target, CopyOption... options)
+            throws IOException {
+        if (this.jrtfs == target.jrtfs) {
+            jrtfs.copyFile(false,
+                    getResolvedPath(), target.getResolvedPath(),
+                    options);
+        } else {
+            copyToTarget(target, options);
+        }
+    }
+
+    private void copyToTarget(AbstractJrtPath target, CopyOption... options)
+            throws IOException {
+        boolean replaceExisting = false;
+        boolean copyAttrs = false;
+        for (CopyOption opt : options) {
+            if (opt == REPLACE_EXISTING) {
+                replaceExisting = true;
+            } else if (opt == COPY_ATTRIBUTES) {
+                copyAttrs = true;
+            }
+        }
+        // attributes of source file
+        BasicFileAttributes jrtfas = getAttributes();
+        // check if target exists
+        boolean exists;
+        if (replaceExisting) {
+            try {
+                target.deleteIfExists();
+                exists = false;
+            } catch (DirectoryNotEmptyException x) {
+                exists = true;
+            }
+        } else {
+            exists = target.exists();
+        }
+        if (exists) {
+            throw new FileAlreadyExistsException(target.toString());
+        }
+
+        if (jrtfas.isDirectory()) {
+            // create directory or file
+            target.createDirectory();
+        } else {
+            try (InputStream is = jrtfs.newInputStream(getResolvedPath()); OutputStream os = target.newOutputStream()) {
+                byte[] buf = new byte[8192];
+                int n;
+                while ((n = is.read(buf)) != -1) {
+                    os.write(buf, 0, n);
+                }
+            }
+        }
+        if (copyAttrs) {
+            BasicFileAttributeView view
+                    = JrtFileAttributeView.get(target, BasicFileAttributeView.class);
+            try {
+                view.setTimes(jrtfas.lastModifiedTime(),
+                        jrtfas.lastAccessTime(),
+                        jrtfas.creationTime());
+            } catch (IOException x) {
+                // rollback?
+                try {
+                    target.delete();
+                } catch (IOException ignore) {
+                }
+                throw x;
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java	Thu Jul 30 23:42:31 2015 +0530
@@ -22,7 +22,6 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package jdk.internal.jrtfs;
 
 import java.nio.file.DirectoryStream;
@@ -34,8 +33,12 @@
 import java.util.NoSuchElementException;
 import java.io.IOException;
 
+/**
+ * DirectoryStream implementation for jrt file system implementations.
+ */
 final class JrtDirectoryStream implements DirectoryStream<Path> {
-    private final JrtFileSystem jrtfs;
+
+    private final AbstractJrtFileSystem jrtfs;
     private final byte[] path;
     // prefix to be used for children of this directory
     // so that child path are reported relatively (if needed)
@@ -44,15 +47,15 @@
     private volatile boolean isClosed;
     private volatile Iterator<Path> itr;
 
-    JrtDirectoryStream(JrtPath jrtPath,
-                       DirectoryStream.Filter<? super java.nio.file.Path> filter)
-        throws IOException
-    {
+    JrtDirectoryStream(AbstractJrtPath jrtPath,
+            DirectoryStream.Filter<? super java.nio.file.Path> filter)
+            throws IOException {
         this.jrtfs = jrtPath.getFileSystem();
         this.path = jrtPath.getResolvedPath();
         // sanity check
-        if (!jrtfs.isDirectory(path, true))
+        if (!jrtfs.isDirectory(path, true)) {
             throw new NotDirectoryException(jrtPath.toString());
+        }
 
         // absolute path and does not have funky chars in front like /./java.base
         if (jrtPath.isAbsolute() && (path.length == jrtPath.getPathLength())) {
@@ -69,10 +72,12 @@
 
     @Override
     public synchronized Iterator<Path> iterator() {
-        if (isClosed)
+        if (isClosed) {
             throw new ClosedDirectoryStreamException();
-        if (itr != null)
+        }
+        if (itr != null) {
             throw new IllegalStateException("Iterator has already been returned");
+        }
 
         try {
             itr = jrtfs.iteratorOf(path, childPrefix);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileAttributes.java	Thu Jul 30 23:42:31 2015 +0530
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * 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.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import jdk.internal.jrtfs.JrtExplodedFileSystem.Node;
+
+/**
+ * jrt file system attributes implementation on top of 'exploded file system'
+ * Node.
+ */
+final class JrtExplodedFileAttributes extends AbstractJrtFileAttributes {
+
+    private final Node node;
+    private final BasicFileAttributes attrs;
+
+    JrtExplodedFileAttributes(Node node) throws IOException {
+        this.node = node;
+        this.attrs = node.getBasicAttrs();
+    }
+
+    @Override
+    public FileTime creationTime() {
+        return attrs.creationTime();
+    }
+
+    @Override
+    public boolean isDirectory() {
+        return node.isDirectory();
+    }
+
+    @Override
+    public boolean isOther() {
+        return false;
+    }
+
+    @Override
+    public boolean isRegularFile() {
+        return node.isFile();
+    }
+
+    @Override
+    public FileTime lastAccessTime() {
+        return attrs.lastAccessTime();
+    }
+
+    @Override
+    public FileTime lastModifiedTime() {
+        return attrs.lastModifiedTime();
+    }
+
+    @Override
+    public long size() {
+        return isRegularFile() ? attrs.size() : 0L;
+    }
+
+    @Override
+    public boolean isSymbolicLink() {
+        return node.isLink();
+    }
+
+    @Override
+    public Object fileKey() {
+        return node.resolveLink(true);
+    }
+
+    @Override
+    public long compressedSize() {
+        return 0L;
+    }
+
+    @Override
+    public String extension() {
+        return node.getExtension();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileSystem.java	Thu Jul 30 23:42:31 2015 +0530
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * 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.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+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 static jdk.internal.jrtfs.AbstractJrtFileSystem.getString;
+
+/**
+ * A jrt file system built on $JAVA_HOME/modules directory ('exploded modules
+ * build')
+ */
+class JrtExplodedFileSystem extends AbstractJrtFileSystem {
+
+    private static final String MODULES = "/modules/";
+    private static final String PACKAGES = "/packages/";
+    private static final int PACKAGES_LEN = PACKAGES.length();
+
+    // root path
+    private final JrtExplodedPath rootPath;
+    private volatile boolean isOpen;
+    private final FileSystem defaultFS;
+    private final String separator;
+    private final Map<String, Node> nodes = Collections.synchronizedMap(new HashMap<>());
+    private final BasicFileAttributes modulesDirAttrs;
+
+    JrtExplodedFileSystem(JrtFileSystemProvider provider,
+            Map<String, ?> env)
+            throws IOException {
+
+        super(provider, env);
+        checkExists(SystemImages.modulesDirPath);
+        byte[] root = new byte[]{'/'};
+        rootPath = new JrtExplodedPath(this, root);
+        isOpen = true;
+        defaultFS = FileSystems.getDefault();
+        String str = defaultFS.getSeparator();
+        separator = str.equals(getSeparator()) ? null : str;
+        modulesDirAttrs = Files.readAttributes(SystemImages.modulesDirPath, BasicFileAttributes.class);
+        initNodes();
+    }
+
+    @Override
+    public void close() throws IOException {
+        cleanup();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return isOpen;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        cleanup();
+        super.finalize();
+    }
+
+    private synchronized void cleanup() {
+        isOpen = false;
+        nodes.clear();
+    }
+
+    @Override
+    JrtExplodedPath getRootPath() {
+        return rootPath;
+    }
+
+    // Base class for Nodes of this file system
+    abstract class Node {
+
+        private final String name;
+
+        Node(String name) {
+            this.name = name;
+        }
+
+        final String getName() {
+            return name;
+        }
+
+        final String getExtension() {
+            if (isFile()) {
+                final int index = name.lastIndexOf(".");
+                if (index != -1) {
+                    return name.substring(index + 1);
+                }
+            }
+
+            return null;
+        }
+
+        BasicFileAttributes getBasicAttrs() throws IOException {
+            return modulesDirAttrs;
+        }
+
+        boolean isLink() {
+            return false;
+        }
+
+        boolean isDirectory() {
+            return false;
+        }
+
+        boolean isFile() {
+            return false;
+        }
+
+        byte[] getContent() throws IOException {
+            if (!isFile()) {
+                throw new FileSystemException(name + " is not file");
+            }
+
+            throw new AssertionError("ShouldNotReachHere");
+        }
+
+        List<Node> getChildren() throws IOException {
+            if (!isDirectory()) {
+                throw new NotDirectoryException(name);
+            }
+
+            throw new AssertionError("ShouldNotReachHere");
+        }
+
+        final Node resolveLink() {
+            return resolveLink(false);
+        }
+
+        Node resolveLink(boolean recursive) {
+            return this;
+        }
+    }
+
+    // A Node that is backed by actual default file system Path
+    private final class PathNode extends Node {
+
+        // Path in underlying default file system
+        private final Path path;
+        private final boolean file;
+        // lazily initialized, don't read attributes unless required!
+        private BasicFileAttributes attrs;
+
+        PathNode(String name, Path path) {
+            super(name);
+            this.path = path;
+            this.file = Files.isRegularFile(path);
+        }
+
+        @Override
+        synchronized BasicFileAttributes getBasicAttrs() throws IOException {
+            if (attrs == null) {
+                attrs = Files.readAttributes(path, BasicFileAttributes.class);
+            }
+            return attrs;
+        }
+
+        @Override
+        boolean isDirectory() {
+            return !file;
+        }
+
+        @Override
+        boolean isFile() {
+            return file;
+        }
+
+        @Override
+        byte[] getContent() throws IOException {
+            if (!isFile()) {
+                throw new FileSystemException(getName() + " is not file");
+            }
+
+            return Files.readAllBytes(path);
+        }
+
+        @Override
+        List<Node> getChildren() throws IOException {
+            if (!isDirectory()) {
+                throw new NotDirectoryException(getName());
+            }
+
+            List<Node> children = new ArrayList<>();
+            try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
+                for (Path cp : stream) {
+                    cp = SystemImages.modulesDirPath.relativize(cp);
+                    String cpName = MODULES + nativeSlashToFrontSlash(cp.toString());
+                    try {
+                        children.add(findNode(cpName));
+                    } catch (NoSuchFileException nsfe) {
+                        // findNode may choose to hide certain files!
+                    }
+                }
+            }
+
+            return children;
+        }
+    }
+
+    // A Node that links to another Node
+    private final class LinkNode extends Node {
+
+        // underlying linked Node
+        private final Node link;
+
+        LinkNode(String name, Node link) {
+            super(name);
+            this.link = link;
+        }
+
+        @Override
+        BasicFileAttributes getBasicAttrs() throws IOException {
+            return link.getBasicAttrs();
+        }
+
+        @Override
+        public boolean isLink() {
+            return true;
+        }
+
+        @Override
+        Node resolveLink(boolean recursive) {
+            return recursive && (link instanceof LinkNode) ? ((LinkNode) link).resolveLink(true) : link;
+        }
+    }
+
+    // A directory Node with it's children Nodes
+    private final class DirNode extends Node {
+
+        // children Nodes of this Node.
+        private final List<Node> children;
+
+        DirNode(String name, List<Node> children) {
+            super(name);
+            this.children = children;
+        }
+
+        @Override
+        boolean isDirectory() {
+            return true;
+        }
+
+        @Override
+        List<Node> getChildren() throws IOException {
+            return children;
+        }
+    }
+
+    private JrtExplodedPath toJrtExplodedPath(String path) {
+        return toJrtExplodedPath(getBytes(path));
+    }
+
+    private JrtExplodedPath toJrtExplodedPath(byte[] path) {
+        return new JrtExplodedPath(this, path);
+    }
+
+    @Override
+    boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException {
+        Node n1 = checkNode(p1.getName());
+        Node n2 = checkNode(p2.getName());
+        return n1 == n2;
+    }
+
+    @Override
+    boolean isLink(AbstractJrtPath jrtPath) throws IOException {
+        return checkNode(jrtPath.getName()).isLink();
+    }
+
+    @Override
+    AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
+        String name = checkNode(jrtPath.getName()).resolveLink().getName();
+        return toJrtExplodedPath(name);
+    }
+
+    @Override
+    AbstractJrtFileAttributes getFileAttributes(byte[] path, LinkOption... options) throws IOException {
+        Node node = checkNode(path);
+        if (node.isLink() && followLinks(options)) {
+            node = node.resolveLink(true);
+        }
+        return new JrtExplodedFileAttributes(node);
+    }
+
+    @Override
+    boolean exists(byte[] path) throws IOException {
+        try {
+            checkNode(path);
+            return true;
+        } catch (NoSuchFileException nsfe) {
+            return false;
+        }
+    }
+
+    @Override
+    boolean isDirectory(byte[] path, boolean resolveLinks) throws IOException {
+        Node node = checkNode(path);
+        return resolveLinks && node.isLink()
+                ? node.resolveLink(true).isDirectory()
+                : node.isDirectory();
+    }
+
+    @Override
+    Iterator<Path> iteratorOf(byte[] path, String childPrefix) throws IOException {
+        Node node = checkNode(path).resolveLink(true);
+        if (!node.isDirectory()) {
+            throw new NotDirectoryException(getString(path));
+        }
+
+        Function<Node, Path> f = childPrefix == null
+                ? child -> toJrtExplodedPath(child.getName())
+                : child -> toJrtExplodedPath(childPrefix + child.getName().substring(1));
+        return node.getChildren().stream().map(f).collect(toList()).iterator();
+    }
+
+    @Override
+    byte[] getFileContent(byte[] path) throws IOException {
+        return checkNode(path).getContent();
+    }
+
+    private Node checkNode(byte[] path) throws IOException {
+        ensureOpen();
+        return findNode(path);
+    }
+
+    synchronized Node findNode(byte[] path) throws IOException {
+        return findNode(getString(path));
+    }
+
+    // find Node for the given Path
+    synchronized Node findNode(String str) throws IOException {
+        Node node = findModulesNode(str);
+        if (node != null) {
+            return node;
+        }
+
+        // lazily created for paths like /packages/<package>/<module>/xyz
+        // For example /packages/java.lang/java.base/java/lang/
+        if (str.startsWith(PACKAGES)) {
+            // pkgEndIdx marks end of <package> part
+            int pkgEndIdx = str.indexOf('/', PACKAGES_LEN);
+            if (pkgEndIdx != -1) {
+                // modEndIdx marks end of <module> part
+                int modEndIdx = str.indexOf('/', pkgEndIdx + 1);
+                if (modEndIdx != -1) {
+                    // make sure we have such module link!
+                    // ie., /packages/<package>/<module> is valid
+                    Node linkNode = nodes.get(str.substring(0, modEndIdx));
+                    if (linkNode == null || !linkNode.isLink()) {
+                        throw new NoSuchFileException(str);
+                    }
+
+                    // map to "/modules/zyz" path and return that node
+                    // For example, "/modules/java.base/java/lang" for
+                    // "/packages/java.lang/java.base/java/lang".
+                    String mod = MODULES + str.substring(pkgEndIdx + 1);
+                    return findNode(mod);
+                }
+            }
+        }
+
+        throw new NoSuchFileException(str);
+    }
+
+    // find a Node for a path that starts like "/modules/..."
+    synchronized Node findModulesNode(String str) throws IOException {
+        Node node = nodes.get(str);
+        if (node != null) {
+            return node;
+        }
+
+        // lazily created "/modules/xyz/abc/" Node
+        // This is mapped to default file system path "<JDK_MODULES_DIR>/xyz/abc"
+        Path p = underlyingPath(str);
+        if (p != null) {
+            if (Files.isRegularFile(p)) {
+                Path file = p.getFileName();
+                if (file.toString().startsWith("_the.")) {
+                    return null;
+                }
+            }
+            node = new PathNode(str, p);
+            nodes.put(str, node);
+            return node;
+        }
+
+        return null;
+    }
+
+    Path underlyingPath(String str) {
+        if (str.startsWith(MODULES)) {
+            str = frontSlashToNativeSlash(str.substring("/modules".length()));
+            return defaultFS.getPath(SystemImages.modulesDirPath.toString(), str);
+        }
+        return null;
+    }
+
+    // convert "/" to platform path separator
+    private String frontSlashToNativeSlash(String str) {
+        return separator != null ? str : str.replace("/", separator);
+    }
+
+    // convert platform path separator to "/"
+    private String nativeSlashToFrontSlash(String str) {
+        return separator != null ? str : str.replace(separator, "/");
+    }
+
+    // convert "/"s to "."s
+    private String slashesToDots(String str) {
+        return str.replace(separator != null ? separator : "/", ".");
+    }
+
+    // initialize file system Nodes
+    private void initNodes() throws IOException {
+        // same package prefix may exist in mutliple modules. This Map
+        // is filled by walking "jdk modules" directory recursively!
+        Map<String, List<String>> packageToModules = new HashMap<>();
+
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(SystemImages.modulesDirPath)) {
+            for (Path module : stream) {
+                if (Files.isDirectory(module)) {
+                    String moduleName = module.getFileName().toString();
+                    // make sure "/modules/<moduleName>" is created
+                    findModulesNode(MODULES + moduleName);
+
+                    Files.walk(module).filter(Files::isDirectory).forEach((p) -> {
+                        p = module.relativize(p);
+                        String pkgName = slashesToDots(p.toString());
+                        // skip META-INFO and empty strings
+                        if (!pkgName.isEmpty() && !pkgName.startsWith("META-INF")) {
+                            List<String> moduleNames = packageToModules.get(pkgName);
+                            if (moduleNames == null) {
+                                moduleNames = new ArrayList<>();
+                                packageToModules.put(pkgName, moduleNames);
+                            }
+                            moduleNames.add(moduleName);
+                        }
+                    });
+                }
+            }
+        }
+
+        // create "/modules" directory
+        // "nodes" map contains only /modules/<foo> nodes only so far and so add all as children of /modules
+        DirNode modulesDir = new DirNode("/modules", new ArrayList<>(nodes.values()));
+        nodes.put(modulesDir.getName(), modulesDir);
+
+        // create children under "/packages"
+        List<Node> packagesChildren = new ArrayList<>(packageToModules.size());
+        for (Map.Entry<String, List<String>> entry : packageToModules.entrySet()) {
+            String pkgName = entry.getKey();
+            List<String> moduleNameList = entry.getValue();
+            List<Node> moduleLinkNodes = new ArrayList<>(moduleNameList.size());
+            for (String moduleName : moduleNameList) {
+                Node moduleNode = findModulesNode(MODULES + moduleName);
+                LinkNode linkNode = new LinkNode(PACKAGES + pkgName + "/" + moduleName, moduleNode);
+                nodes.put(linkNode.getName(), linkNode);
+                moduleLinkNodes.add(linkNode);
+            }
+
+            DirNode pkgDir = new DirNode(PACKAGES + pkgName, moduleLinkNodes);
+            nodes.put(pkgDir.getName(), pkgDir);
+            packagesChildren.add(pkgDir);
+        }
+
+        // "/packages" dir
+        DirNode packagesDir = new DirNode("/packages", packagesChildren);
+        nodes.put(packagesDir.getName(), packagesDir);
+
+        // finally "/" dir!
+        List<Node> rootChildren = new ArrayList<>();
+        rootChildren.add(modulesDir);
+        rootChildren.add(packagesDir);
+        DirNode root = new DirNode("/", rootChildren);
+        nodes.put(root.getName(), root);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedPath.java	Thu Jul 30 23:42:31 2015 +0530
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jrtfs;
+
+/**
+ * Path implementation for jrt file system on JDK exploded modules build.
+ */
+final class JrtExplodedPath extends AbstractJrtPath {
+
+    JrtExplodedPath(AbstractJrtFileSystem jrtfs, byte[] path) {
+        super(jrtfs, path);
+    }
+
+    JrtExplodedPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
+        super(jrtfs, path, normalized);
+    }
+
+    @Override
+    protected AbstractJrtPath newJrtPath(byte[] path) {
+        return new JrtExplodedPath(jrtfs, path);
+    }
+
+    @Override
+    protected AbstractJrtPath newJrtPath(byte[] path, boolean normalized) {
+        return new JrtExplodedPath(jrtfs, path, normalized);
+    }
+
+    @Override
+    public JrtExplodedFileSystem getFileSystem() {
+        return (JrtExplodedFileSystem) jrtfs;
+    }
+}
--- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java	Thu Jul 30 23:42:31 2015 +0530
@@ -22,7 +22,6 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package jdk.internal.jrtfs;
 
 import java.nio.file.LinkOption;
@@ -31,9 +30,13 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-final class JrtFileAttributeView implements BasicFileAttributeView
-{
+/**
+ * File attribute view for jrt file system.
+ */
+final class JrtFileAttributeView implements BasicFileAttributeView {
+
     private static enum AttrID {
+
         size,
         creationTime,
         lastAccessTime,
@@ -47,124 +50,133 @@
         extension
     };
 
-    private final JrtPath path;
+    private final AbstractJrtPath path;
     private final boolean isJrtView;
     private final LinkOption[] options;
 
-    private JrtFileAttributeView(JrtPath path, boolean isJrtView, LinkOption... options) {
+    private JrtFileAttributeView(AbstractJrtPath path, boolean isJrtView, LinkOption... options) {
         this.path = path;
         this.isJrtView = isJrtView;
         this.options = options;
     }
 
     @SuppressWarnings("unchecked") // Cast to V
-    static <V extends FileAttributeView> V get(JrtPath path, Class<V> type, LinkOption... options) {
-        if (type == null)
+    static <V extends FileAttributeView> V get(AbstractJrtPath path, Class<V> type, LinkOption... options) {
+        if (type == null) {
             throw new NullPointerException();
-        if (type == BasicFileAttributeView.class)
-            return (V)new JrtFileAttributeView(path, false, options);
-        if (type == JrtFileAttributeView.class)
-            return (V)new JrtFileAttributeView(path, true, options);
+        }
+        if (type == BasicFileAttributeView.class) {
+            return (V) new JrtFileAttributeView(path, false, options);
+        }
+        if (type == JrtFileAttributeView.class) {
+            return (V) new JrtFileAttributeView(path, true, options);
+        }
         return null;
     }
 
-    static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) {
-        if (type == null)
+    static JrtFileAttributeView get(AbstractJrtPath path, String type, LinkOption... options) {
+        if (type == null) {
             throw new NullPointerException();
-        if (type.equals("basic"))
+        }
+        if (type.equals("basic")) {
             return new JrtFileAttributeView(path, false, options);
-        if (type.equals("jjrt"))
+        }
+        if (type.equals("jrt")) {
             return new JrtFileAttributeView(path, true, options);
+        }
         return null;
     }
 
     @Override
     public String name() {
-        return isJrtView ? "jjrt" : "basic";
+        return isJrtView ? "jrt" : "basic";
     }
 
     @Override
-    public JrtFileAttributes readAttributes() throws IOException
-    {
+    public AbstractJrtFileAttributes readAttributes() throws IOException {
         return path.getAttributes(options);
     }
 
     @Override
     public void setTimes(FileTime lastModifiedTime,
-                         FileTime lastAccessTime,
-                         FileTime createTime)
-        throws IOException
-    {
+            FileTime lastAccessTime,
+            FileTime createTime)
+            throws IOException {
         path.setTimes(lastModifiedTime, lastAccessTime, createTime);
     }
 
     void setAttribute(String attribute, Object value)
-        throws IOException
-    {
+            throws IOException {
         try {
-            if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime)
-                setTimes ((FileTime)value, null, null);
-            if (AttrID.valueOf(attribute) == AttrID.lastAccessTime)
-                setTimes (null, (FileTime)value, null);
-            if (AttrID.valueOf(attribute) == AttrID.creationTime)
-                setTimes (null, null, (FileTime)value);
+            if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime) {
+                setTimes((FileTime) value, null, null);
+            }
+            if (AttrID.valueOf(attribute) == AttrID.lastAccessTime) {
+                setTimes(null, (FileTime) value, null);
+            }
+            if (AttrID.valueOf(attribute) == AttrID.creationTime) {
+                setTimes(null, null, (FileTime) value);
+            }
             return;
-        } catch (IllegalArgumentException x) {}
-        throw new UnsupportedOperationException("'" + attribute +
-            "' is unknown or read-only attribute");
+        } catch (IllegalArgumentException x) {
+        }
+        throw new UnsupportedOperationException("'" + attribute
+                + "' is unknown or read-only attribute");
     }
 
     Map<String, Object> readAttributes(String attributes)
-        throws IOException
-    {
-        JrtFileAttributes jrtfas = readAttributes();
+            throws IOException {
+        AbstractJrtFileAttributes jrtfas = readAttributes();
         LinkedHashMap<String, Object> map = new LinkedHashMap<>();
         if ("*".equals(attributes)) {
             for (AttrID id : AttrID.values()) {
                 try {
                     map.put(id.name(), attribute(id, jrtfas));
-                } catch (IllegalArgumentException x) {}
+                } catch (IllegalArgumentException x) {
+                }
             }
         } else {
             String[] as = attributes.split(",");
             for (String a : as) {
                 try {
                     map.put(a, attribute(AttrID.valueOf(a), jrtfas));
-                } catch (IllegalArgumentException x) {}
+                } catch (IllegalArgumentException x) {
+                }
             }
         }
         return map;
     }
 
-    Object attribute(AttrID id, JrtFileAttributes jrtfas) {
+    Object attribute(AttrID id, AbstractJrtFileAttributes jrtfas) {
         switch (id) {
-        case size:
-            return jrtfas.size();
-        case creationTime:
-            return jrtfas.creationTime();
-        case lastAccessTime:
-            return jrtfas.lastAccessTime();
-        case lastModifiedTime:
-            return jrtfas.lastModifiedTime();
-        case isDirectory:
-            return jrtfas.isDirectory();
-        case isRegularFile:
-            return jrtfas.isRegularFile();
-        case isSymbolicLink:
-            return jrtfas.isSymbolicLink();
-        case isOther:
-            return jrtfas.isOther();
-        case fileKey:
-            return jrtfas.fileKey();
-        case compressedSize:
-            if (isJrtView)
-                return jrtfas.compressedSize();
-            break;
-        case extension:
-            if (isJrtView) {
-                return jrtfas.extension();
-            }
-            break;
+            case size:
+                return jrtfas.size();
+            case creationTime:
+                return jrtfas.creationTime();
+            case lastAccessTime:
+                return jrtfas.lastAccessTime();
+            case lastModifiedTime:
+                return jrtfas.lastModifiedTime();
+            case isDirectory:
+                return jrtfas.isDirectory();
+            case isRegularFile:
+                return jrtfas.isRegularFile();
+            case isSymbolicLink:
+                return jrtfas.isSymbolicLink();
+            case isOther:
+                return jrtfas.isOther();
+            case fileKey:
+                return jrtfas.fileKey();
+            case compressedSize:
+                if (isJrtView) {
+                    return jrtfas.compressedSize();
+                }
+                break;
+            case extension:
+                if (isJrtView) {
+                    return jrtfas.extension();
+                }
+                break;
         }
         return null;
     }
--- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java	Thu Jul 30 23:42:31 2015 +0530
@@ -22,16 +22,16 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package jdk.internal.jrtfs;
 
-import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileTime;
-import java.util.Formatter;
 import jdk.internal.jimage.ImageReader.Node;
 
-final class JrtFileAttributes implements BasicFileAttributes
-{
+/**
+ * File attributes implementation for jrt image file system.
+ */
+final class JrtFileAttributes extends AbstractJrtFileAttributes {
+
     private final Node node;
 
     JrtFileAttributes(Node node) {
@@ -85,37 +85,13 @@
     }
 
     ///////// jrt entry attributes ///////////
+    @Override
     public long compressedSize() {
         return node.compressedSize();
     }
 
+    @Override
     public String extension() {
         return node.extension();
     }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(1024);
-        try (Formatter fm = new Formatter(sb)) {
-            if (creationTime() != null)
-                fm.format("    creationTime    : %tc%n", creationTime().toMillis());
-            else
-                fm.format("    creationTime    : null%n");
-
-            if (lastAccessTime() != null)
-                fm.format("    lastAccessTime  : %tc%n", lastAccessTime().toMillis());
-            else
-                fm.format("    lastAccessTime  : null%n");
-            fm.format("    lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
-            fm.format("    isRegularFile   : %b%n", isRegularFile());
-            fm.format("    isDirectory     : %b%n", isDirectory());
-            fm.format("    isSymbolicLink  : %b%n", isSymbolicLink());
-            fm.format("    isOther         : %b%n", isOther());
-            fm.format("    fileKey         : %s%n", fileKey());
-            fm.format("    size            : %d%n", size());
-            fm.format("    compressedSize  : %d%n", compressedSize());
-            fm.format("    extension       : %s%n", extension());
-        }
-        return sb.toString();
-    }
 }
--- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java	Thu Jul 30 23:42:31 2015 +0530
@@ -22,23 +22,23 @@
  * 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.Files;
 import java.nio.file.FileStore;
-import java.nio.file.FileSystems;
-import java.nio.file.Path;
+import java.nio.file.FileSystem;
 import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.BasicFileAttributeView;
 import java.nio.file.attribute.FileStoreAttributeView;
-import java.nio.file.attribute.BasicFileAttributeView;
 
+/**
+ * File store implementation for jrt file systems.
+ */
 final class JrtFileStore extends FileStore {
 
-    private final JrtFileSystem jrtfs;
+    protected final FileSystem jrtfs;
 
-    JrtFileStore(JrtPath jrtPath) {
+    JrtFileStore(AbstractJrtPath jrtPath) {
         this.jrtfs = jrtPath.getFileSystem();
     }
 
@@ -58,12 +58,6 @@
     }
 
     @Override
-    public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
-        return (type == BasicFileAttributeView.class ||
-                type == JrtFileAttributeView.class);
-    }
-
-    @Override
     public boolean supportsFileAttributeView(String name) {
         return name.equals("basic") || name.equals("jrt");
     }
@@ -71,28 +65,35 @@
     @Override
     @SuppressWarnings("unchecked")
     public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
-        if (type == null)
+        if (type == null) {
             throw new NullPointerException();
-        return (V)null;
+        }
+        return (V) null;
     }
 
     @Override
     public long getTotalSpace() throws IOException {
-         throw new UnsupportedOperationException("getTotalSpace");
+        throw new UnsupportedOperationException("getTotalSpace");
     }
 
     @Override
     public long getUsableSpace() throws IOException {
-         throw new UnsupportedOperationException("getUsableSpace");
+        throw new UnsupportedOperationException("getUsableSpace");
     }
 
     @Override
     public long getUnallocatedSpace() throws IOException {
-         throw new UnsupportedOperationException("getUnallocatedSpace");
+        throw new UnsupportedOperationException("getUnallocatedSpace");
     }
 
     @Override
     public Object getAttribute(String attribute) throws IOException {
-         throw new UnsupportedOperationException("does not support " + attribute);
+        throw new UnsupportedOperationException("does not support " + attribute);
+    }
+
+    @Override
+    public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
+        return (type == BasicFileAttributeView.class
+                || type == JrtFileAttributeView.class);
     }
 }
--- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java	Thu Jul 30 23:42:31 2015 +0530
@@ -24,68 +24,35 @@
  */
 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.*;
-import java.nio.charset.Charset;
-import java.nio.file.ClosedFileSystemException;
-import java.nio.file.CopyOption;
 import java.nio.file.LinkOption;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
 import java.nio.file.FileSystemException;
-import java.nio.file.FileSystemNotFoundException;
-import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
 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.Set;
 import java.util.function.Function;
-import java.util.regex.Pattern;
 import static java.util.stream.Collectors.toList;
 import jdk.internal.jimage.ImageReader;
 import jdk.internal.jimage.ImageReader.Node;
 import jdk.internal.jimage.UTF8String;
 
 /**
- * A FileSystem built on System jimage files.
+ * jrt file system implementation built on System jimage files.
  */
-class JrtFileSystem extends FileSystem {
-    private static final Charset UTF_8 = Charset.forName("UTF-8");
+class JrtFileSystem extends AbstractJrtFileSystem {
 
-    private final JrtFileSystemProvider provider;
-    // System image readers
+    // System image reader
     private ImageReader bootImage;
     // root path
     private final JrtPath rootPath;
     private volatile boolean isOpen;
 
-    private static void checkExists(Path path) {
-        if (Files.notExists(path)) {
-            throw new FileSystemNotFoundException(path.toString());
-        }
-    }
-
     // open a .jimage and build directory structure
     private static ImageReader openImage(Path path) throws IOException {
         ImageReader image = ImageReader.open(path.toString());
@@ -96,27 +63,18 @@
     JrtFileSystem(JrtFileSystemProvider provider,
             Map<String, ?> env)
             throws IOException {
-        this.provider = provider;
+        super(provider, env);
         checkExists(SystemImages.bootImagePath);
 
         // open image file
         this.bootImage = openImage(SystemImages.bootImagePath);
 
-        byte[] root = new byte[] { '/' };
+        byte[] root = new byte[]{'/'};
         rootPath = new JrtPath(this, root);
         isOpen = true;
     }
 
-    @Override
-    public FileSystemProvider provider() {
-        return provider;
-    }
-
-    @Override
-    public String getSeparator() {
-        return "/";
-    }
-
+    // FileSystem method implementations
     @Override
     public boolean isOpen() {
         return isOpen;
@@ -128,19 +86,108 @@
     }
 
     @Override
-    protected void finalize() {
+    protected void finalize() throws Throwable {
         try {
             cleanup();
-        } catch (IOException ignored) {}
+        } catch (IOException ignored) {
+        }
+        super.finalize();
     }
 
+    // AbstractJrtFileSystem method implementations
+    @Override
+    JrtPath getRootPath() {
+        return rootPath;
+    }
+
+    @Override
+    boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException {
+        ensureOpen();
+        NodeAndImage n1 = findNode(p1.getName());
+        NodeAndImage n2 = findNode(p2.getName());
+        return n1.node.equals(n2.node);
+    }
+
+    @Override
+    boolean isLink(AbstractJrtPath jrtPath) throws IOException {
+        return checkNode(jrtPath.getName()).node.isLink();
+    }
+
+    @Override
+    AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
+        NodeAndImage ni = checkNode(jrtPath.getName());
+        if (ni.node.isLink()) {
+            Node node = ni.node.resolveLink();
+            return toJrtPath(node.getName().getBytesCopy());
+        }
+
+        return jrtPath;
+    }
+
+    @Override
+    JrtFileAttributes getFileAttributes(byte[] path, LinkOption... options)
+            throws IOException {
+        NodeAndImage ni = checkNode(path);
+        if (ni.node.isLink() && followLinks(options)) {
+            return new JrtFileAttributes(ni.node.resolveLink(true));
+        }
+        return new JrtFileAttributes(ni.node);
+    }
+
+    @Override
+    boolean exists(byte[] path) throws IOException {
+        try {
+            checkNode(path);
+        } catch (NoSuchFileException exp) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    boolean isDirectory(byte[] path, boolean resolveLinks)
+            throws IOException {
+        NodeAndImage ni = checkNode(path);
+        return resolveLinks && ni.node.isLink()
+                ? ni.node.resolveLink(true).isDirectory()
+                : ni.node.isDirectory();
+    }
+
+    @Override
+    Iterator<Path> iteratorOf(byte[] path, String childPrefix)
+            throws IOException {
+        NodeAndImage ni = checkNode(path);
+        Node node = ni.node.resolveLink(true);
+
+        if (!node.isDirectory()) {
+            throw new NotDirectoryException(getString(path));
+        }
+
+        if (node.isRootDir()) {
+            return rootDirIterator(path, childPrefix);
+        } else if (node.isModulesDir()) {
+            return modulesDirIterator(path, childPrefix);
+        } else if (node.isPackagesDir()) {
+            return packagesDirIterator(path, childPrefix);
+        }
+
+        return nodesToIterator(toJrtPath(path), childPrefix, node.getChildren());
+    }
+
+    @Override
+    byte[] getFileContent(byte[] path) throws IOException {
+        final NodeAndImage ni = checkResource(path);
+        return ni.getResource();
+    }
+
+    // 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) {
+        synchronized (this) {
             isOpen = false;
 
             // close all image reader and null out
@@ -149,129 +196,14 @@
         }
     }
 
-    private void ensureOpen() throws IOException {
-        if (!isOpen) {
-            throw new ClosedFileSystemException();
-        }
-    }
+    private static class NodeAndImage {
 
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    private ReadOnlyFileSystemException readOnly() {
-        return new ReadOnlyFileSystemException();
-    }
-
-    @Override
-    public Iterable<Path> getRootDirectories() {
-        ArrayList<Path> pathArr = new ArrayList<>();
-        pathArr.add(rootPath);
-        return pathArr;
-    }
-
-    JrtPath getRootPath() {
-        return rootPath;
-    }
-
-    @Override
-    public JrtPath getPath(String first, String... more) {
-        String path;
-        if (more.length == 0) {
-            path = first;
-        } else {
-            StringBuilder sb = new StringBuilder();
-            sb.append(first);
-            for (String segment : more) {
-                if (segment.length() > 0) {
-                    if (sb.length() > 0) {
-                        sb.append('/');
-                    }
-                    sb.append(segment);
-                }
-            }
-            path = sb.toString();
-        }
-        return new JrtPath(this, getBytes(path));
-    }
-
-    @Override
-    public UserPrincipalLookupService getUserPrincipalLookupService() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public WatchService newWatchService() {
-        throw new UnsupportedOperationException();
-    }
-
-    FileStore getFileStore(JrtPath path) {
-        return new JrtFileStore(path);
-    }
-
-    @Override
-    public Iterable<FileStore> getFileStores() {
-        ArrayList<FileStore> list = new ArrayList<>(1);
-        list.add(new JrtFileStore(new JrtPath(this, new byte[]{'/'})));
-        return list;
-    }
-
-    private static final Set<String> supportedFileAttributeViews
-            = Collections.unmodifiableSet(
-                    new HashSet<String>(Arrays.asList("basic", "jrt")));
-
-    @Override
-    public Set<String> supportedFileAttributeViews() {
-        return supportedFileAttributeViews;
-    }
-
-    @Override
-    public String toString() {
-        return "jrt:/";
-    }
-
-    private static final String GLOB_SYNTAX = "glob";
-    private static final String REGEX_SYNTAX = "regex";
-
-    @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_SYNTAX)) {
-            expr = JrtUtils.toRegexPattern(input);
-        } else {
-            if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) {
-                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();
-    }
-
-    static byte[] getBytes(String name) {
-        return name.getBytes(UTF_8);
-    }
-
-    static String getString(byte[] name) {
-        return new String(name, UTF_8);
-    }
-
-    private static class NodeAndImage {
         final Node node;
         final ImageReader image;
 
         NodeAndImage(Node node, ImageReader image) {
-            this.node = node; this.image = image;
+            this.node = node;
+            this.image = image;
         }
 
         byte[] getResource() throws IOException {
@@ -287,12 +219,12 @@
         } catch (RuntimeException re) {
             throw new InvalidPathException(getString(path), re.toString());
         }
-        return node != null? new NodeAndImage(node, image) : null;
+        return node != null ? new NodeAndImage(node, image) : null;
     }
 
     private NodeAndImage lookupSymbolic(byte[] path) {
         for (int i = 1; i < path.length; i++) {
-            if (path[i] == (byte)'/') {
+            if (path[i] == (byte) '/') {
                 byte[] prefix = Arrays.copyOfRange(path, 0, i);
                 NodeAndImage ni = lookup(prefix);
                 if (ni == null) {
@@ -305,7 +237,7 @@
                     UTF8String resPath = link.getName().concat(new UTF8String(path, i));
                     byte[] resPathBytes = resPath.getBytesCopy();
                     ni = lookup(resPathBytes);
-                    return ni != null? ni : lookupSymbolic(resPathBytes);
+                    return ni != null ? ni : lookupSymbolic(resPathBytes);
                 }
             }
         }
@@ -339,127 +271,23 @@
         return ni;
     }
 
-    static boolean followLinks(LinkOption... options) {
-        if (options != null) {
-            for (LinkOption lo : options) {
-                if (lo == LinkOption.NOFOLLOW_LINKS) {
-                    return false;
-                } else if (lo == null) {
-                    throw new NullPointerException();
-                } else {
-                    throw new AssertionError("should not reach here");
-                }
-            }
-        }
-        return true;
-    }
-
-    // package private helpers
-    JrtFileAttributes getFileAttributes(byte[] path, LinkOption... options)
-            throws IOException {
-        NodeAndImage ni = checkNode(path);
-        if (ni.node.isLink() && followLinks(options)) {
-            return new JrtFileAttributes(ni.node.resolveLink(true));
-        }
-        return new JrtFileAttributes(ni.node);
-    }
-
-    void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime)
-            throws IOException {
-        throw readOnly();
-    }
-
-    boolean exists(byte[] path) throws IOException {
-        ensureOpen();
-        try {
-            findNode(path);
-        } catch (NoSuchFileException exp) {
-            return false;
-        }
-        return true;
-    }
-
-    boolean isDirectory(byte[] path, boolean resolveLinks)
-            throws IOException {
-        ensureOpen();
-        NodeAndImage ni = checkNode(path);
-        return resolveLinks && ni.node.isLink()?
-            ni.node.resolveLink(true).isDirectory() :
-            ni.node.isDirectory();
-    }
-
-    JrtPath toJrtPath(String path) {
+    private JrtPath toJrtPath(String path) {
         return toJrtPath(getBytes(path));
     }
 
-    JrtPath toJrtPath(byte[] path) {
+    private JrtPath toJrtPath(byte[] path) {
         return new JrtPath(this, path);
     }
 
-    boolean isSameFile(JrtPath p1, JrtPath p2) throws IOException {
-        NodeAndImage n1 = findNode(p1.getName());
-        NodeAndImage n2 = findNode(p2.getName());
-        return n1.node.equals(n2.node);
-    }
-
-    boolean isLink(JrtPath jrtPath) throws IOException {
-        return findNode(jrtPath.getName()).node.isLink();
-    }
-
-    JrtPath resolveLink(JrtPath jrtPath) throws IOException {
-        NodeAndImage ni = findNode(jrtPath.getName());
-        if (ni.node.isLink()) {
-            Node node = ni.node.resolveLink();
-            return toJrtPath(node.getName().getBytesCopy());
-        }
-
-        return jrtPath;
-    }
-
-    /**
-     * returns the list of child paths of the given directory "path"
-     *
-     * @param path name of the directory whose content is listed
-     * @param childPrefix prefix added to returned children names - may be null
-              in which case absolute child paths are returned
-     * @return iterator for child paths of the given directory path
-     */
-    Iterator<Path> iteratorOf(byte[] path, String childPrefix)
-            throws IOException {
-        NodeAndImage ni = checkNode(path);
-        Node node = ni.node.resolveLink(true);
-
-        if (!node.isDirectory()) {
-            throw new NotDirectoryException(getString(path));
-        }
-
-        if (node.isRootDir()) {
-            return rootDirIterator(path, childPrefix);
-        } else if (node.isModulesDir()) {
-            return modulesDirIterator(path, childPrefix);
-        } else if (node.isPackagesDir()) {
-            return packagesDirIterator(path, childPrefix);
-        }
-
-        return nodesToIterator(toJrtPath(path), childPrefix, node.getChildren());
-    }
-
     private Iterator<Path> nodesToIterator(Path path, String childPrefix, List<Node> childNodes) {
         Function<Node, Path> f = childPrefix == null
                 ? child -> toJrtPath(child.getNameString())
                 : child -> toJrtPath(childPrefix + child.getNameString().substring(1));
-         return childNodes.stream().map(f).collect(toList()).iterator();
-    }
-
-    private void addRootDirContent(List<Node> children) {
-        for (Node child : children) {
-            if (!(child.isModulesDir() || child.isPackagesDir())) {
-                rootChildren.add(child);
-            }
-        }
+        return childNodes.stream().map(f).collect(toList()).iterator();
     }
 
     private List<Node> rootChildren;
+
     private synchronized void initRootChildren(byte[] path) {
         if (rootChildren == null) {
             rootChildren = new ArrayList<>();
@@ -473,6 +301,7 @@
     }
 
     private List<Node> modulesChildren;
+
     private synchronized void initModulesChildren(byte[] path) {
         if (modulesChildren == null) {
             modulesChildren = new ArrayList<>();
@@ -486,127 +315,16 @@
     }
 
     private List<Node> packagesChildren;
+
     private synchronized void initPackagesChildren(byte[] path) {
         if (packagesChildren == null) {
             packagesChildren = new ArrayList<>();
             packagesChildren.addAll(bootImage.findNode(path).getChildren());
         }
     }
+
     private Iterator<Path> packagesDirIterator(byte[] path, String childPrefix) throws IOException {
         initPackagesChildren(path);
         return nodesToIterator(new JrtPath(this, path), childPrefix, packagesChildren);
     }
-
-    void createDirectory(byte[] dir, FileAttribute<?>... attrs)
-            throws IOException {
-        throw readOnly();
-    }
-
-    void copyFile(boolean deletesrc, byte[] src, byte[] dst, CopyOption... options)
-            throws IOException {
-        throw readOnly();
-    }
-
-    public void deleteFile(byte[] path, boolean failIfNotExists)
-            throws IOException {
-        throw readOnly();
-    }
-
-    OutputStream newOutputStream(byte[] path, OpenOption... options)
-            throws IOException {
-        throw readOnly();
-    }
-
-    private void checkOptions(Set<? extends OpenOption> options) {
-        // check for options of null type and option is an intance of StandardOpenOption
-        for (OpenOption option : options) {
-            if (option == null) {
-                throw new NullPointerException();
-            }
-            if (!(option instanceof StandardOpenOption)) {
-                throw new IllegalArgumentException();
-            }
-        }
-    }
-
-    // Returns an input stream for reading the contents of the specified
-    // file entry.
-    InputStream newInputStream(byte[] path) throws IOException {
-        final NodeAndImage ni = checkResource(path);
-        return new ByteArrayInputStream(ni.getResource());
-    }
-
-    SeekableByteChannel newByteChannel(byte[] path,
-            Set<? extends OpenOption> options,
-            FileAttribute<?>... attrs)
-            throws IOException {
-        checkOptions(options);
-        if (options.contains(StandardOpenOption.WRITE)
-                || options.contains(StandardOpenOption.APPEND)) {
-            throw readOnly();
-        }
-
-        NodeAndImage ni = checkResource(path);
-        byte[] buf = ni.getResource();
-        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();
-            }
-        };
-    }
-
-    // Returns a FileChannel of the specified path.
-    FileChannel newFileChannel(byte[] path,
-            Set<? extends OpenOption> options,
-            FileAttribute<?>... attrs)
-            throws IOException {
-        throw new UnsupportedOperationException("newFileChannel");
-    }
 }
--- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java	Thu Jul 30 23:42:31 2015 +0530
@@ -22,7 +22,6 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package jdk.internal.jrtfs;
 
 import java.io.*;
@@ -32,16 +31,20 @@
 import java.nio.file.attribute.*;
 import java.nio.file.spi.FileSystemProvider;
 import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
+/**
+ * File system provider for jrt file systems. Conditionally creates jrt fs on
+ * .jimage file or exploded modules directory of underlying JDK.
+ */
 public final class JrtFileSystemProvider extends FileSystemProvider {
+
     private volatile FileSystem theFileSystem;
 
-    public JrtFileSystemProvider() { }
+    public JrtFileSystemProvider() {
+    }
 
     @Override
     public String getScheme() {
@@ -55,50 +58,60 @@
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             String home = SystemImages.RUNTIME_HOME;
-            FilePermission perm =
-                new FilePermission(home + File.separator + "-", "read");
+            FilePermission perm
+                    = new FilePermission(home + File.separator + "-", "read");
             sm.checkPermission(perm);
         }
     }
 
     private void checkUri(URI uri) {
-        if (!uri.getScheme().equalsIgnoreCase(getScheme()))
+        if (!uri.getScheme().equalsIgnoreCase(getScheme())) {
             throw new IllegalArgumentException("URI does not match this provider");
-        if (uri.getAuthority() != null)
+        }
+        if (uri.getAuthority() != null) {
             throw new IllegalArgumentException("Authority component present");
-        if (uri.getPath() == null)
+        }
+        if (uri.getPath() == null) {
             throw new IllegalArgumentException("Path component is undefined");
-        if (!uri.getPath().equals("/"))
+        }
+        if (!uri.getPath().equals("/")) {
             throw new IllegalArgumentException("Path component should be '/'");
-        if (uri.getQuery() != null)
+        }
+        if (uri.getQuery() != null) {
             throw new IllegalArgumentException("Query component present");
-        if (uri.getFragment() != null)
+        }
+        if (uri.getFragment() != null) {
             throw new IllegalArgumentException("Fragment component present");
+        }
     }
 
     @Override
     public FileSystem newFileSystem(URI uri, Map<String, ?> env)
-        throws IOException
-    {
+            throws IOException {
         checkPermission();
         checkUri(uri);
-        return new JrtFileSystem(this, env);
+        return SystemImages.bootImageExists ? new JrtFileSystem(this, env) : new JrtExplodedFileSystem(this, env);
     }
 
     @Override
     public Path getPath(URI uri) {
         checkPermission();
-        if (!uri.getScheme().equalsIgnoreCase(getScheme()))
+        if (!uri.getScheme().equalsIgnoreCase(getScheme())) {
             throw new IllegalArgumentException("URI does not match this provider");
-        if (uri.getAuthority() != null)
+        }
+        if (uri.getAuthority() != null) {
             throw new IllegalArgumentException("Authority component present");
-        if (uri.getQuery() != null)
+        }
+        if (uri.getQuery() != null) {
             throw new IllegalArgumentException("Query component present");
-        if (uri.getFragment() != null)
+        }
+        if (uri.getFragment() != null) {
             throw new IllegalArgumentException("Fragment component present");
+        }
         String path = uri.getPath();
-        if (path == null || path.charAt(0) != '/')
+        if (path == null || path.charAt(0) != '/') {
             throw new IllegalArgumentException("Invalid path component");
+        }
         return getTheFileSystem().getPath(path);
     }
 
@@ -110,11 +123,21 @@
                 fs = this.theFileSystem;
                 if (fs == null) {
                     try {
-                        this.theFileSystem = fs = new JrtFileSystem(this, null) {
-                            @Override public void close() {
-                                throw new UnsupportedOperationException();
-                            }
-                        };
+                        if (SystemImages.bootImageExists) {
+                            this.theFileSystem = fs = new JrtFileSystem(this, null) {
+                                @Override
+                                public void close() {
+                                    throw new UnsupportedOperationException();
+                                }
+                            };
+                        } else {
+                            this.theFileSystem = fs = new JrtExplodedFileSystem(this, null) {
+                                @Override
+                                public void close() {
+                                    throw new UnsupportedOperationException();
+                                }
+                            };
+                        }
                     } catch (IOException ioe) {
                         throw new InternalError(ioe);
                     }
@@ -132,71 +155,69 @@
     }
 
     // Checks that the given file is a JrtPath
-    static final JrtPath toJrtPath(Path path) {
-        if (path == null)
+    static final AbstractJrtPath toAbstractJrtPath(Path path) {
+        if (path == null) {
             throw new NullPointerException();
-        if (!(path instanceof JrtPath))
+        }
+        if (!(path instanceof AbstractJrtPath)) {
             throw new ProviderMismatchException();
-        return (JrtPath)path;
+        }
+        return (AbstractJrtPath) path;
     }
 
     @Override
     public void checkAccess(Path path, AccessMode... modes) throws IOException {
-        toJrtPath(path).checkAccess(modes);
+        toAbstractJrtPath(path).checkAccess(modes);
     }
 
     @Override
     public Path readSymbolicLink(Path link) throws IOException {
-        return toJrtPath(link).readSymbolicLink();
+        return toAbstractJrtPath(link).readSymbolicLink();
     }
 
     @Override
     public void copy(Path src, Path target, CopyOption... options)
-        throws IOException
-    {
-        toJrtPath(src).copy(toJrtPath(target), options);
+            throws IOException {
+        toAbstractJrtPath(src).copy(toAbstractJrtPath(target), options);
     }
 
     @Override
     public void createDirectory(Path path, FileAttribute<?>... attrs)
-        throws IOException
-    {
-        toJrtPath(path).createDirectory(attrs);
+            throws IOException {
+        toAbstractJrtPath(path).createDirectory(attrs);
     }
 
     @Override
     public final void delete(Path path) throws IOException {
-        toJrtPath(path).delete();
+        toAbstractJrtPath(path).delete();
     }
 
     @Override
     @SuppressWarnings("unchecked")
     public <V extends FileAttributeView> V
-        getFileAttributeView(Path path, Class<V> type, LinkOption... options)
-    {
-        return JrtFileAttributeView.get(toJrtPath(path), type, options);
+            getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
+        return JrtFileAttributeView.get(toAbstractJrtPath(path), type, options);
     }
 
     @Override
     public FileStore getFileStore(Path path) throws IOException {
-        return toJrtPath(path).getFileStore();
+        return toAbstractJrtPath(path).getFileStore();
     }
 
     @Override
     public boolean isHidden(Path path) {
-        return toJrtPath(path).isHidden();
+        return toAbstractJrtPath(path).isHidden();
     }
 
     @Override
     public boolean isSameFile(Path path, Path other) throws IOException {
-        return toJrtPath(path).isSameFile(other);
+        return toAbstractJrtPath(path).isSameFile(other);
     }
 
     @Override
     public void move(Path src, Path target, CopyOption... options)
-        throws IOException
-    {
-        toJrtPath(src).move(toJrtPath(target), options);
+            throws IOException {
+        toAbstractJrtPath(src).move(toAbstractJrtPath(target), options);
     }
 
     @Override
@@ -204,74 +225,66 @@
             Set<? extends OpenOption> options,
             ExecutorService exec,
             FileAttribute<?>... attrs)
-            throws IOException
-    {
+            throws IOException {
         throw new UnsupportedOperationException();
     }
 
     @Override
     public SeekableByteChannel newByteChannel(Path path,
-                                              Set<? extends OpenOption> options,
-                                              FileAttribute<?>... attrs)
-        throws IOException
-    {
-        return toJrtPath(path).newByteChannel(options, attrs);
+            Set<? extends OpenOption> options,
+            FileAttribute<?>... attrs)
+            throws IOException {
+        return toAbstractJrtPath(path).newByteChannel(options, attrs);
     }
 
     @Override
     public DirectoryStream<Path> newDirectoryStream(
-        Path path, Filter<? super Path> filter) throws IOException
-    {
-        return toJrtPath(path).newDirectoryStream(filter);
+            Path path, Filter<? super Path> filter) throws IOException {
+        return toAbstractJrtPath(path).newDirectoryStream(filter);
     }
 
     @Override
     public FileChannel newFileChannel(Path path,
-                                      Set<? extends OpenOption> options,
-                                      FileAttribute<?>... attrs)
-        throws IOException
-    {
-        return toJrtPath(path).newFileChannel(options, attrs);
+            Set<? extends OpenOption> options,
+            FileAttribute<?>... attrs)
+            throws IOException {
+        return toAbstractJrtPath(path).newFileChannel(options, attrs);
     }
 
     @Override
     public InputStream newInputStream(Path path, OpenOption... options)
-        throws IOException
-    {
-        return toJrtPath(path).newInputStream(options);
+            throws IOException {
+        return toAbstractJrtPath(path).newInputStream(options);
     }
 
     @Override
     public OutputStream newOutputStream(Path path, OpenOption... options)
-        throws IOException
-    {
-        return toJrtPath(path).newOutputStream(options);
+            throws IOException {
+        return toAbstractJrtPath(path).newOutputStream(options);
     }
 
     @Override
     @SuppressWarnings("unchecked") // Cast to A
     public <A extends BasicFileAttributes> A
-        readAttributes(Path path, Class<A> type, LinkOption... options)
-        throws IOException
-    {
-        if (type == BasicFileAttributes.class || type == JrtFileAttributes.class)
-            return (A)toJrtPath(path).getAttributes(options);
+            readAttributes(Path path, Class<A> type, LinkOption... options)
+            throws IOException {
+        if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) {
+            return (A) toAbstractJrtPath(path).getAttributes(options);
+        }
         return null;
     }
 
     @Override
     public Map<String, Object>
-        readAttributes(Path path, String attribute, LinkOption... options)
-        throws IOException
-    {
-        return toJrtPath(path).readAttributes(attribute, options);
+            readAttributes(Path path, String attribute, LinkOption... options)
+            throws IOException {
+        return toAbstractJrtPath(path).readAttributes(attribute, options);
     }
 
     @Override
     public void setAttribute(Path path, String attribute,
-                             Object value, LinkOption... options)
-        throws IOException
-    {
-        toJrtPath(path).setAttribute(attribute, value, options);
+            Object value, LinkOption... options)
+            throws IOException {
+        toAbstractJrtPath(path).setAttribute(attribute, value, options);
     }
 }
--- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java	Thu Jul 30 23:42:31 2015 +0530
@@ -22,848 +22,28 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package jdk.internal.jrtfs;
 
-import java.io.*;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.channels.*;
-import java.nio.file.*;
-import java.nio.file.DirectoryStream.Filter;
-import java.nio.file.attribute.*;
-import java.util.*;
-import static java.nio.file.StandardOpenOption.*;
-import static java.nio.file.StandardCopyOption.*;
+/**
+ * jrt Path implementation for jrt on .jimage files.
+ */
+final class JrtPath extends AbstractJrtPath {
 
-final class JrtPath implements Path {
-
-    private final JrtFileSystem jrtfs;
-    private final byte[] path;
-    private volatile int[] offsets;
-    private int hashcode = 0;  // cached hashcode (created lazily)
-
-    JrtPath(JrtFileSystem jrtfs, byte[] path) {
+    JrtPath(AbstractJrtFileSystem jrtfs, byte[] path) {
         this(jrtfs, path, false);
     }
 
-    JrtPath(JrtFileSystem jrtfs, byte[] path, boolean normalized) {
-        this.jrtfs = jrtfs;
-        if (normalized)
-            this.path = path;
-        else
-            this.path = normalize(path);
-    }
-
-    byte[] getName() {
-        return path;
+    JrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
+        super(jrtfs, path, normalized);
     }
 
     @Override
-    public JrtPath getRoot() {
-        if (this.isAbsolute())
-            return jrtfs.getRootPath();
-        else
-            return null;
+    protected JrtPath newJrtPath(byte[] path) {
+        return new JrtPath(jrtfs, path);
     }
 
     @Override
-    public Path getFileName() {
-        initOffsets();
-        int count = offsets.length;
-        if (count == 0)
-            return null;  // no elements so no name
-        if (count == 1 && path[0] != '/')
-            return this;
-        int lastOffset = offsets[count-1];
-        int len = path.length - lastOffset;
-        byte[] result = new byte[len];
-        System.arraycopy(path, lastOffset, result, 0, len);
-        return new JrtPath(jrtfs, result);
-    }
-
-    @Override
-    public JrtPath getParent() {
-        initOffsets();
-        int count = offsets.length;
-        if (count == 0)    // no elements so no parent
-            return null;
-        int len = offsets[count-1] - 1;
-        if (len <= 0)      // parent is root only (may be null)
-            return getRoot();
-        byte[] result = new byte[len];
-        System.arraycopy(path, 0, result, 0, len);
-        return new JrtPath(jrtfs, result);
-    }
-
-    @Override
-    public int getNameCount() {
-        initOffsets();
-        return offsets.length;
-    }
-
-    @Override
-    public JrtPath getName(int index) {
-        initOffsets();
-        if (index < 0 || index >= offsets.length)
-            throw new IllegalArgumentException();
-        int begin = offsets[index];
-        int len;
-        if (index == (offsets.length-1))
-            len = path.length - begin;
-        else
-            len = offsets[index+1] - begin - 1;
-        // construct result
-        byte[] result = new byte[len];
-        System.arraycopy(path, begin, result, 0, len);
-        return new JrtPath(jrtfs, result);
-    }
-
-    @Override
-    public JrtPath subpath(int beginIndex, int endIndex) {
-        initOffsets();
-        if (beginIndex < 0 ||
-            beginIndex >=  offsets.length ||
-            endIndex > offsets.length ||
-            beginIndex >= endIndex)
-            throw new IllegalArgumentException();
-
-        // starting offset and length
-        int begin = offsets[beginIndex];
-        int len;
-        if (endIndex == offsets.length)
-            len = path.length - begin;
-        else
-            len = offsets[endIndex] - begin - 1;
-        // construct result
-        byte[] result = new byte[len];
-        System.arraycopy(path, begin, result, 0, len);
-        return new JrtPath(jrtfs, result);
-    }
-
-    @Override
-    public JrtPath toRealPath(LinkOption... options) throws IOException {
-        JrtPath realPath = new JrtPath(jrtfs, getResolvedPath()).toAbsolutePath();
-        realPath = JrtFileSystem.followLinks(options)? jrtfs.resolveLink(this) : realPath;
-        realPath.checkAccess();
-        return realPath;
-    }
-
-    JrtPath readSymbolicLink() throws IOException {
-        if (! jrtfs.isLink(this)) {
-           throw new IOException("not a symbolic link");
-        }
-
-        return jrtfs.resolveLink(this);
-    }
-
-    boolean isHidden() {
-        return false;
-    }
-
-    @Override
-    public JrtPath toAbsolutePath() {
-        if (isAbsolute()) {
-            return this;
-        } else {
-            //add / bofore the existing path
-            byte[] tmp = new byte[path.length + 1];
-            tmp[0] = '/';
-            System.arraycopy(path, 0, tmp, 1, path.length);
-            return (JrtPath) new JrtPath(jrtfs, tmp).normalize();
-        }
-    }
-
-    @Override
-    public URI toUri() {
-        try {
-            return new URI("jrt",
-                           JrtFileSystem.getString(toAbsolutePath().path),
-                           null);
-        } catch (URISyntaxException ex) {
-            throw new AssertionError(ex);
-        }
-    }
-
-    private boolean equalsNameAt(JrtPath other, int index) {
-        int mbegin = offsets[index];
-        int mlen;
-        if (index == (offsets.length-1))
-            mlen = path.length - mbegin;
-        else
-            mlen = offsets[index + 1] - mbegin - 1;
-        int obegin = other.offsets[index];
-        int olen;
-        if (index == (other.offsets.length - 1))
-            olen = other.path.length - obegin;
-        else
-            olen = other.offsets[index + 1] - obegin - 1;
-        if (mlen != olen)
-            return false;
-        int n = 0;
-        while(n < mlen) {
-            if (path[mbegin + n] != other.path[obegin + n])
-                return false;
-            n++;
-        }
-        return true;
-    }
-
-    @Override
-    public Path relativize(Path other) {
-        final JrtPath o = checkPath(other);
-        if (o.equals(this))
-            return new JrtPath(getFileSystem(), new byte[0], true);
-        if (/* this.getFileSystem() != o.getFileSystem() || */
-            this.isAbsolute() != o.isAbsolute()) {
-            throw new IllegalArgumentException();
-        }
-        int mc = this.getNameCount();
-        int oc = o.getNameCount();
-        int n = Math.min(mc, oc);
-        int i = 0;
-        while (i < n) {
-            if (!equalsNameAt(o, i))
-                break;
-            i++;
-        }
-        int dotdots = mc - i;
-        int len = dotdots * 3 - 1;
-        if (i < oc)
-            len += (o.path.length - o.offsets[i] + 1);
-        byte[] result = new byte[len];
-
-        int pos = 0;
-        while (dotdots > 0) {
-            result[pos++] = (byte)'.';
-            result[pos++] = (byte)'.';
-            if (pos < len)       // no tailing slash at the end
-                result[pos++] = (byte)'/';
-            dotdots--;
-        }
-        if (i < oc)
-            System.arraycopy(o.path, o.offsets[i],
-                             result, pos,
-                             o.path.length - o.offsets[i]);
-        return new JrtPath(getFileSystem(), result);
-    }
-
-    @Override
-    public JrtFileSystem getFileSystem() {
-        return jrtfs;
-    }
-
-    @Override
-    public boolean isAbsolute() {
-        return (this.path.length > 0 && path[0] == '/');
-    }
-
-    @Override
-    public JrtPath resolve(Path other) {
-        final JrtPath o = checkPath(other);
-        if (o.isAbsolute())
-            return o;
-        byte[] res;
-        if (this.path[path.length - 1] == '/') {
-            res = new byte[path.length + o.path.length];
-            System.arraycopy(path, 0, res, 0, path.length);
-            System.arraycopy(o.path, 0, res, path.length, o.path.length);
-        } else {
-            res = new byte[path.length + 1 + o.path.length];
-            System.arraycopy(path, 0, res, 0, path.length);
-            res[path.length] = '/';
-            System.arraycopy(o.path, 0, res, path.length + 1, o.path.length);
-        }
-        return new JrtPath(jrtfs, res);
-    }
-
-    @Override
-    public Path resolveSibling(Path other) {
-        if (other == null)
-            throw new NullPointerException();
-        Path parent = getParent();
-        return (parent == null) ? other : parent.resolve(other);
-    }
-
-    @Override
-    public boolean startsWith(Path other) {
-        final JrtPath o = checkPath(other);
-        if (o.isAbsolute() != this.isAbsolute() ||
-            o.path.length > this.path.length)
-            return false;
-        int olast = o.path.length;
-        for (int i = 0; i < olast; i++) {
-            if (o.path[i] != this.path[i])
-                return false;
-        }
-        olast--;
-        return o.path.length == this.path.length ||
-               o.path[olast] == '/' ||
-               this.path[olast + 1] == '/';
-    }
-
-    @Override
-    public boolean endsWith(Path other) {
-        final JrtPath o = checkPath(other);
-        int olast = o.path.length - 1;
-        if (olast > 0 && o.path[olast] == '/')
-            olast--;
-        int last = this.path.length - 1;
-        if (last > 0 && this.path[last] == '/')
-            last--;
-        if (olast == -1)    // o.path.length == 0
-            return last == -1;
-        if ((o.isAbsolute() &&(!this.isAbsolute() || olast != last)) ||
-            (last < olast))
-            return false;
-        for (; olast >= 0; olast--, last--) {
-            if (o.path[olast] != this.path[last])
-                return false;
-        }
-        return o.path[olast + 1] == '/' ||
-               last == -1 || this.path[last] == '/';
-    }
-
-    @Override
-    public JrtPath resolve(String other) {
-        return resolve(getFileSystem().getPath(other));
-    }
-
-    @Override
-    public final Path resolveSibling(String other) {
-        return resolveSibling(getFileSystem().getPath(other));
-    }
-
-    @Override
-    public final boolean startsWith(String other) {
-        return startsWith(getFileSystem().getPath(other));
-    }
-
-    @Override
-    public final boolean endsWith(String other) {
-        return endsWith(getFileSystem().getPath(other));
-    }
-
-    @Override
-    public Path normalize() {
-        byte[] res = getResolved();
-        if (res == path)    // no change
-            return this;
-        return new JrtPath(jrtfs, res, true);
-    }
-
-    private JrtPath checkPath(Path path) {
-        if (path == null)
-            throw new NullPointerException();
-        if (!(path instanceof JrtPath))
-            throw new ProviderMismatchException();
-        return (JrtPath) path;
-    }
-
-    // create offset list if not already created
-    private void initOffsets() {
-        if (offsets == null) {
-            int count, index;
-            // count names
-            count = 0;
-            index = 0;
-            while (index < path.length) {
-                byte c = path[index++];
-                if (c != '/') {
-                    count++;
-                    while (index < path.length && path[index] != '/')
-                        index++;
-                }
-            }
-            // populate offsets
-            int[] result = new int[count];
-            count = 0;
-            index = 0;
-            while (index < path.length) {
-                byte c = path[index];
-                if (c == '/') {
-                    index++;
-                } else {
-                    result[count++] = index++;
-                    while (index < path.length && path[index] != '/')
-                        index++;
-                }
-            }
-            synchronized (this) {
-                if (offsets == null)
-                    offsets = result;
-            }
-        }
-    }
-
-    // resolved path for locating jrt entry inside the jrt file,
-    // the result path does not contain ./ and .. components
-    // resolved bytes will always start with '/'
-    private volatile byte[] resolved = null;
-    byte[] getResolvedPath() {
-        byte[] r = resolved;
-        if (r == null) {
-            if (isAbsolute())
-                r = getResolved();
-            else
-                r = toAbsolutePath().getResolvedPath();
-            resolved = r;
-        }
-        return resolved;
-    }
-
-    // removes redundant slashs, replace "\" to separator "/"
-    // and check for invalid characters
-    private static byte[] normalize(byte[] path) {
-        if (path.length == 0)
-            return path;
-        byte prevC = 0;
-        for (int i = 0; i < path.length; i++) {
-            byte c = path[i];
-            if (c == '\\')
-                return normalize(path, i);
-            if (c == (byte)'/' && prevC == '/')
-                return normalize(path, i - 1);
-            if (c == '\u0000')
-                throw new InvalidPathException(JrtFileSystem.getString(path),
-                                               "Path: nul character not allowed");
-            prevC = c;
-        }
-
-        if (path.length > 1 && path[path.length - 1] == '/') {
-            return Arrays.copyOf(path, path.length - 1);
-        }
-
-        return path;
-    }
-
-    private static byte[] normalize(byte[] path, int off) {
-        byte[] to = new byte[path.length];
-        int n = 0;
-        while (n < off) {
-            to[n] = path[n];
-            n++;
-        }
-        int m = n;
-        byte prevC = 0;
-        while (n < path.length) {
-            byte c = path[n++];
-            if (c == (byte)'\\')
-                c = (byte)'/';
-            if (c == (byte)'/' && prevC == (byte)'/')
-                continue;
-            if (c == '\u0000')
-                throw new InvalidPathException(JrtFileSystem.getString(path),
-                                               "Path: nul character not allowed");
-            to[m++] = c;
-            prevC = c;
-        }
-        if (m > 1 && to[m - 1] == '/')
-            m--;
-        return (m == to.length)? to : Arrays.copyOf(to, m);
-    }
-
-    // Remove DotSlash(./) and resolve DotDot (..) components
-    private byte[] getResolved() {
-        if (path.length == 0)
-            return path;
-        for (int i = 0; i < path.length; i++) {
-            byte c = path[i];
-            if (c == (byte)'.')
-                return resolve0();
-        }
-
-        return path;
-    }
-
-    // TBD: performance, avoid initOffsets
-    private byte[] resolve0() {
-        byte[] to = new byte[path.length];
-        int nc = getNameCount();
-        int[] lastM = new int[nc];
-        int lastMOff = -1;
-        int m = 0;
-        for (int i = 0; i < nc; i++) {
-            int n = offsets[i];
-            int len = (i == offsets.length - 1)?
-                      (path.length - n):(offsets[i + 1] - n - 1);
-            if (len == 1 && path[n] == (byte)'.') {
-                if (m == 0 && path[0] == '/')   // absolute path
-                    to[m++] = '/';
-                continue;
-            }
-            if (len == 2 && path[n] == '.' && path[n + 1] == '.') {
-                if (lastMOff >= 0) {
-                    m = lastM[lastMOff--];  // retreat
-                    continue;
-                }
-                if (path[0] == '/') {  // "/../xyz" skip
-                    if (m == 0)
-                        to[m++] = '/';
-                } else {               // "../xyz" -> "../xyz"
-                    if (m != 0 && to[m-1] != '/')
-                        to[m++] = '/';
-                    while (len-- > 0)
-                        to[m++] = path[n++];
-                }
-                continue;
-            }
-            if (m == 0 && path[0] == '/' ||   // absolute path
-                m != 0 && to[m-1] != '/') {   // not the first name
-                to[m++] = '/';
-            }
-            lastM[++lastMOff] = m;
-            while (len-- > 0)
-                to[m++] = path[n++];
-        }
-        if (m > 1 && to[m - 1] == '/')
-            m--;
-        return (m == to.length)? to : Arrays.copyOf(to, m);
-    }
-
-    @Override
-    public String toString() {
-        return JrtFileSystem.getString(path);
-    }
-
-    @Override
-    public int hashCode() {
-        int h = hashcode;
-        if (h == 0)
-            hashcode = h = Arrays.hashCode(path);
-        return h;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return obj != null &&
-               obj instanceof JrtPath &&
-               this.jrtfs == ((JrtPath)obj).jrtfs &&
-               compareTo((Path) obj) == 0;
-    }
-
-    @Override
-    public int compareTo(Path other) {
-        final JrtPath o = checkPath(other);
-        int len1 = this.path.length;
-        int len2 = o.path.length;
-
-        int n = Math.min(len1, len2);
-        byte v1[] = this.path;
-        byte v2[] = o.path;
-
-        int k = 0;
-        while (k < n) {
-            int c1 = v1[k] & 0xff;
-            int c2 = v2[k] & 0xff;
-            if (c1 != c2)
-                return c1 - c2;
-            k++;
-        }
-        return len1 - len2;
-    }
-
-    @Override
-    public WatchKey register(
-            WatchService watcher,
-            WatchEvent.Kind<?>[] events,
-            WatchEvent.Modifier... modifiers) {
-        if (watcher == null || events == null || modifiers == null) {
-            throw new NullPointerException();
-        }
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
-        return register(watcher, events, new WatchEvent.Modifier[0]);
-    }
-
-    @Override
-    public final File toFile() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Iterator<Path> iterator() {
-        return new Iterator<Path>() {
-            private int i = 0;
-
-            @Override
-            public boolean hasNext() {
-                return (i < getNameCount());
-            }
-
-            @Override
-            public Path next() {
-                if (i < getNameCount()) {
-                    Path result = getName(i);
-                    i++;
-                    return result;
-                } else {
-                    throw new NoSuchElementException();
-                }
-            }
-
-            @Override
-            public void remove() {
-                throw new ReadOnlyFileSystemException();
-            }
-        };
-    }
-
-    /////////////////////////////////////////////////////////////////////
-    // Helpers for JrtFileSystemProvider and JrtFileSystem
-
-    int getPathLength() {
-        return path.length;
-    }
-
-
-    void createDirectory(FileAttribute<?>... attrs)
-        throws IOException
-    {
-        jrtfs.createDirectory(getResolvedPath(), attrs);
-    }
-
-    InputStream newInputStream(OpenOption... options) throws IOException
-    {
-        if (options.length > 0) {
-            for (OpenOption opt : options) {
-                if (opt != READ)
-                    throw new UnsupportedOperationException("'" + opt + "' not allowed");
-            }
-        }
-        return jrtfs.newInputStream(getResolvedPath());
-    }
-
-    DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
-        throws IOException
-    {
-        return new JrtDirectoryStream(this, filter);
-    }
-
-    void delete() throws IOException {
-        jrtfs.deleteFile(getResolvedPath(), true);
-    }
-
-    void deleteIfExists() throws IOException {
-        jrtfs.deleteFile(getResolvedPath(), false);
-    }
-
-    JrtFileAttributes getAttributes(LinkOption... options) throws IOException
-    {
-        JrtFileAttributes zfas = jrtfs.getFileAttributes(getResolvedPath(), options);
-        if (zfas == null)
-            throw new NoSuchFileException(toString());
-        return zfas;
-    }
-
-    void setAttribute(String attribute, Object value, LinkOption... options)
-        throws IOException
-    {
-        String type;
-        String attr;
-        int colonPos = attribute.indexOf(':');
-        if (colonPos == -1) {
-            type = "basic";
-            attr = attribute;
-        } else {
-            type = attribute.substring(0, colonPos++);
-            attr = attribute.substring(colonPos);
-        }
-        JrtFileAttributeView view = JrtFileAttributeView.get(this, type, options);
-        if (view == null)
-            throw new UnsupportedOperationException("view <" + view + "> is not supported");
-        view.setAttribute(attr, value);
-    }
-
-    void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
-        throws IOException
-    {
-        jrtfs.setTimes(getResolvedPath(), mtime, atime, ctime);
-    }
-
-    Map<String, Object> readAttributes(String attributes, LinkOption... options)
-        throws IOException
-
-    {
-        String view;
-        String attrs;
-        int colonPos = attributes.indexOf(':');
-        if (colonPos == -1) {
-            view = "basic";
-            attrs = attributes;
-        } else {
-            view = attributes.substring(0, colonPos++);
-            attrs = attributes.substring(colonPos);
-        }
-        JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view, options);
-        if (jrtfv == null) {
-            throw new UnsupportedOperationException("view not supported");
-        }
-        return jrtfv.readAttributes(attrs);
-    }
-
-    FileStore getFileStore() throws IOException {
-        // each JrtFileSystem only has one root (as requested for now)
-        if (exists())
-            return jrtfs.getFileStore(this);
-        throw new NoSuchFileException(JrtFileSystem.getString(path));
-    }
-
-    boolean isSameFile(Path other) throws IOException {
-        if (this.equals(other))
-            return true;
-        if (other == null ||
-            this.getFileSystem() != other.getFileSystem())
-            return false;
-        this.checkAccess();
-        JrtPath path = (JrtPath)other;
-        path.checkAccess();
-        return Arrays.equals(this.getResolvedPath(), path.getResolvedPath()) ||
-            jrtfs.isSameFile(this, (JrtPath)other);
-    }
-
-    SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
-                                       FileAttribute<?>... attrs)
-        throws IOException
-    {
-        return jrtfs.newByteChannel(getResolvedPath(), options, attrs);
-    }
-
-
-    FileChannel newFileChannel(Set<? extends OpenOption> options,
-                               FileAttribute<?>... attrs)
-        throws IOException
-    {
-        return jrtfs.newFileChannel(getResolvedPath(), options, attrs);
-    }
-
-    void checkAccess(AccessMode... modes) throws IOException {
-        boolean w = false;
-        boolean x = false;
-        for (AccessMode mode : modes) {
-            switch (mode) {
-                case READ:
-                    break;
-                case WRITE:
-                    w = true;
-                    break;
-                case EXECUTE:
-                    x = true;
-                    break;
-                default:
-                    throw new UnsupportedOperationException();
-            }
-        }
-        JrtFileAttributes attrs = jrtfs.getFileAttributes(getResolvedPath());
-        if (attrs == null && (path.length != 1 || path[0] != '/'))
-            throw new NoSuchFileException(toString());
-        if (w) {
-            if (jrtfs.isReadOnly())
-                throw new AccessDeniedException(toString());
-        }
-        if (x)
-            throw new AccessDeniedException(toString());
-    }
-
-    boolean exists() {
-        if (isAbsolute())
-            return true;
-        try {
-            return jrtfs.exists(getResolvedPath());
-        } catch (IOException x) {}
-        return false;
-    }
-
-    OutputStream newOutputStream(OpenOption... options) throws IOException
-    {
-        if (options.length == 0)
-            return jrtfs.newOutputStream(getResolvedPath(),
-                                       CREATE_NEW, WRITE);
-        return jrtfs.newOutputStream(getResolvedPath(), options);
-    }
-
-    void move(JrtPath target, CopyOption... options)
-        throws IOException
-    {
-        if (this.jrtfs == target.jrtfs)
-        {
-            jrtfs.copyFile(true,
-                         getResolvedPath(), target.getResolvedPath(),
-                         options);
-        } else {
-            copyToTarget(target, options);
-            delete();
-        }
-    }
-
-    void copy(JrtPath target, CopyOption... options)
-        throws IOException
-    {
-        if (this.jrtfs == target.jrtfs)
-            jrtfs.copyFile(false,
-                         getResolvedPath(), target.getResolvedPath(),
-                         options);
-        else
-            copyToTarget(target, options);
-    }
-
-    private void copyToTarget(JrtPath target, CopyOption... options)
-        throws IOException
-    {
-        boolean replaceExisting = false;
-        boolean copyAttrs = false;
-        for (CopyOption opt : options) {
-            if (opt == REPLACE_EXISTING)
-                replaceExisting = true;
-            else if (opt == COPY_ATTRIBUTES)
-                copyAttrs = true;
-        }
-        // attributes of source file
-        JrtFileAttributes jrtfas = getAttributes();
-        // check if target exists
-        boolean exists;
-        if (replaceExisting) {
-            try {
-                target.deleteIfExists();
-                exists = false;
-            } catch (DirectoryNotEmptyException x) {
-                exists = true;
-            }
-        } else {
-            exists = target.exists();
-        }
-        if (exists)
-            throw new FileAlreadyExistsException(target.toString());
-
-        if (jrtfas.isDirectory()) {
-            // create directory or file
-            target.createDirectory();
-        } else {
-            try (InputStream is = jrtfs.newInputStream(getResolvedPath()); OutputStream os = target.newOutputStream()) {
-                byte[] buf = new byte[8192];
-                int n;
-                while ((n = is.read(buf)) != -1) {
-                    os.write(buf, 0, n);
-                }
-            }
-        }
-        if (copyAttrs) {
-            BasicFileAttributeView view =
-                JrtFileAttributeView.get(target, BasicFileAttributeView.class);
-            try {
-                view.setTimes(jrtfas.lastModifiedTime(),
-                              jrtfas.lastAccessTime(),
-                              jrtfas.creationTime());
-            } catch (IOException x) {
-                // rollback?
-                try {
-                    target.delete();
-                } catch (IOException ignore) { }
-                throw x;
-            }
-        }
+    protected JrtPath newJrtPath(byte[] path, boolean normalized) {
+        return new JrtPath(jrtfs, path, normalized);
     }
 }
--- a/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java	Thu Jul 30 23:42:31 2015 +0530
@@ -22,11 +22,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package jdk.internal.jrtfs;
 
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.nio.file.Files;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.Path;
@@ -38,8 +38,14 @@
 final class SystemImages {
     private SystemImages() {}
 
+
     static final String RUNTIME_HOME;
+    // bootmodules.jimage file Path
     static final Path bootImagePath;
+    // <JAVA_HOME>/modules directory Path
+    static final Path modulesDirPath;
+    // bootmodules.jimage exists or not?
+    static final boolean bootImageExists;
 
     static {
         PrivilegedAction<String> pa = SystemImages::findHome;
@@ -47,8 +53,12 @@
 
         FileSystem fs = FileSystems.getDefault();
         bootImagePath = fs.getPath(RUNTIME_HOME, "lib", "modules", "bootmodules.jimage");
+        modulesDirPath = fs.getPath(RUNTIME_HOME, "modules");
+
+        bootImageExists = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> Files.exists(bootImagePath));
     }
 
+
     /**
      * Returns the appropriate JDK home for this usage of the FileSystemProvider.
      * When the CodeSource is null (null loader) then jrt:/ is the current runtime,
--- a/test/jdk/internal/jrtfs/Basic.java	Thu Jul 30 12:33:38 2015 +0100
+++ b/test/jdk/internal/jrtfs/Basic.java	Thu Jul 30 23:42:31 2015 +0530
@@ -384,6 +384,8 @@
             { "/modules/java.base/packages.offsets" },
             { "/modules/java.instrument/packages.offsets" },
             { "/modules/jdk.zipfs/packages.offsets" },
+            { "/modules/java.base/_the.java.base.vardeps" },
+            { "/modules/java.base/_the.java.base_batch" },
             { "/java/lang" },
             { "/java/util" },
         };
@@ -573,7 +575,9 @@
         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
         InvalidPathException ipe = null;
         try {
-            Files.exists(fs.getPath("/packages/\ud834\udd7b"));
+            boolean res = Files.exists(fs.getPath("/packages/\ud834\udd7b"));
+            assertFalse(res);
+            return;
         } catch (InvalidPathException e) {
             ipe = e;
         }