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

Print this page




   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.internal.jrtfs;
  26 

  27 import java.io.IOException;
  28 import java.nio.file.LinkOption;












  29 import java.nio.file.FileSystemException;
  30 import java.nio.file.InvalidPathException;

  31 import java.nio.file.NoSuchFileException;
  32 import java.nio.file.NotDirectoryException;

  33 import java.nio.file.Path;








  34 import java.util.ArrayList;
  35 import java.util.Arrays;


  36 import java.util.Iterator;
  37 import java.util.List;
  38 import java.util.Map;
  39 import java.util.function.Function;
  40 import static java.util.stream.Collectors.toList;
  41 import jdk.internal.jimage.ImageReader;
  42 import jdk.internal.jimage.ImageReader.Node;
  43 
  44 
  45 /**
  46  * jrt file system implementation built on System jimage files.
  47  *
  48  * @implNote This class needs to maintain JDK 8 source compatibility.
  49  *
  50  * It is used internally in the JDK to implement jimage/jrtfs access,
  51  * but also compiled and delivered as part of the jrtfs.jar to support access
  52  * to the jimage file provided by the shipped JDK by tools running on JDK 8.
  53  */
  54 class JrtFileSystem extends AbstractJrtFileSystem {
  55 
  56     // System image reader
  57     private ImageReader bootImage;
  58     // root path
  59     private final JrtPath rootPath;
  60     private volatile boolean isOpen;


  61 
  62     // open a .jimage and build directory structure
  63     private static ImageReader openImage(Path path) throws IOException {
  64         ImageReader image = ImageReader.open(path);
  65         image.getRootDirectory();
  66         return image;
  67     }
  68 
  69     JrtFileSystem(JrtFileSystemProvider provider,
  70             Map<String, ?> env)
  71             throws IOException {
  72         super(provider, env);
  73         checkExists(SystemImages.moduleImageFile());
  74 
  75         // open image file
  76         this.bootImage = openImage(SystemImages.moduleImageFile());
  77 
  78         byte[] root = new byte[]{'/'};
  79         rootPath = new JrtPath(this, root);
  80         isOpen = true;
  81     }
  82 
  83     // FileSystem method implementations
  84     @Override
  85     public boolean isOpen() {
  86         return isOpen;
  87     }
  88 
  89     @Override
  90     public void close() throws IOException {


  91         cleanup();
  92     }
  93 
  94     @Override
  95     protected void finalize() throws Throwable {
  96         try {
  97             cleanup();
  98         } catch (IOException ignored) {
  99         }
 100         super.finalize();



 101     }
 102 
 103     // AbstractJrtFileSystem method implementations
 104     @Override
 105     JrtPath getRootPath() {
 106         return rootPath;


 107     }
 108 
 109     @Override
 110     boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException {
 111         ensureOpen();
 112         Node node1 = findNode(p1);
 113         Node node2 = findNode(p2);
 114         return node1.equals(node2);










 115     }
 116 
 117     @Override
 118     boolean isLink(AbstractJrtPath jrtPath) throws IOException {
 119         return checkNode(jrtPath).isLink();
 120     }
 121 
 122     @Override
 123     AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
 124         Node node = checkNode(jrtPath);
 125         if (node.isLink()) {
 126             node = node.resolveLink();
 127             return toJrtPath(getBytes(node.getName()));
 128         }
 129 
 130         return jrtPath;


 131     }
 132 
 133     @Override
 134     JrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options)
 135             throws IOException {
 136         Node node = checkNode(jrtPath);
 137         if (node.isLink() && followLinks(options)) {
 138             return new JrtFileAttributes(node.resolveLink(true));
 139         }
 140         return new JrtFileAttributes(node);







 141     }
 142 
 143     @Override
 144     boolean exists(AbstractJrtPath jrtPath) throws IOException {
 145         try {
 146             checkNode(jrtPath);
 147         } catch (NoSuchFileException exp) {
 148             return false;
 149         }
 150         return true;



 151     }
 152 
 153     @Override
 154     boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks)






























 155             throws IOException {
 156         Node node = checkNode(jrtPath);
 157         return resolveLinks && node.isLink()
 158                 ? node.resolveLink(true).isDirectory()
 159                 : node.isDirectory();

 160     }
 161 
 162     @Override
 163     Iterator<Path> iteratorOf(AbstractJrtPath jrtPath) throws IOException {
 164         Node node = checkNode(jrtPath).resolveLink(true);






 165         if (!node.isDirectory()) {
 166             throw new NotDirectoryException(getString(jrtPath.getName()));















 167         }
 168 
 169         if (node.isRootDir()) {
 170             return rootDirIterator(jrtPath);
 171         } else if (node.isModulesDir()) {
 172             return modulesDirIterator(jrtPath);
 173         } else if (node.isPackagesDir()) {
 174             return packagesDirIterator(jrtPath);


 175         }
 176 
 177         return nodesToIterator(jrtPath, node.getChildren());




 178     }
 179 
 180     @Override
 181     byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException {
 182         final Node node = checkResource(jrtPath);
 183         return bootImage.getResource(node);
























 184     }
 185 
 186     // Implementation details below this point
 187     // clean up this file system - called from finalize and close
 188     private void cleanup() throws IOException {
 189         if (!isOpen) {
 190             return;
 191         }
 192 
 193         synchronized (this) {
 194             isOpen = false;
 195 
 196             // close all image reader and null out
 197             bootImage.close();
 198             bootImage = null;
 199         }
 200     }
 201 
 202     private Node lookup(byte[] path) {
 203         Node node = null;
 204         try {
 205             node = bootImage.findNode(getString(path));
 206         } catch (RuntimeException re) {
 207             throw new InvalidPathException(getString(path), re.toString());
 208         }
 209         return node;



 210     }
 211 
 212     private Node lookupSymbolic(byte[] path) {
 213         for (int i = 1; i < path.length; i++) {
 214             if (path[i] == (byte) '/') {
 215                 byte[] prefix = Arrays.copyOfRange(path, 0, i);
 216                 Node node = lookup(prefix);
 217                 if (node == null) {
 218                     break;
 219                 }
 220 
 221                 if (node.isLink()) {
 222                     Node link = node.resolveLink(true);
 223                     // resolved symbolic path concatenated to the rest of the path
 224                     String resPath = link.getName() + getString(path).substring(i);
 225                     byte[] resPathBytes = getBytes(resPath);
 226                     node = lookup(resPathBytes);
 227                     return node != null ? node : lookupSymbolic(resPathBytes);
 228                 }




 229             }






 230         }
 231 
 232         return null;

 233     }
 234 
 235     private Node findNode(AbstractJrtPath jrtPath) throws IOException {
 236         return findNode(jrtPath.getResolvedPath());














 237     }
 238 
 239     private Node findNode(byte[] path) throws IOException {
 240         Node node = lookup(path);
 241         if (node == null) {
 242             node = lookupSymbolic(path);
 243             if (node == null) {
 244                 throw new NoSuchFileException(getString(path));



 245             }






 246         }
 247         return node;
 248     }
 249 
 250     private Node checkNode(AbstractJrtPath jrtPath) throws IOException {
 251         return checkNode(jrtPath.getResolvedPath());


 252     }
 253 
 254     private Node checkNode(byte[] path) throws IOException {
 255         ensureOpen();
 256         return findNode(path);
 257     }
 258 
 259     private Node checkResource(AbstractJrtPath jrtPath) throws IOException {
 260         return checkResource(jrtPath.getResolvedPath());

 261     }
 262 
 263     private Node checkResource(byte[] path) throws IOException {
 264         Node node = checkNode(path);
 265         if (node.isDirectory()) {
 266             throw new FileSystemException(getString(path) + " is a directory");

 267         }
 268 
 269         assert node.isResource() : "resource node expected here";
 270         return node;
 271     }
 272 
 273     private JrtPath toJrtPath(String path) {
 274         return toJrtPath(getBytes(path));


 275     }
 276 
 277     private JrtPath toJrtPath(byte[] path) {
 278         return new JrtPath(this, path);
 279     }
 280 
 281     private Iterator<Path> nodesToIterator(AbstractJrtPath dir, List<Node> childNodes) {
 282         Function<Node, Path> nodeToPath =
 283             child -> dir.resolve(
 284                 toJrtPath(child.getNameString()).getFileName());
 285         return childNodes.stream().
 286                 map(nodeToPath).collect(toList()).
 287                 iterator();
 288     }
 289 
 290     private List<Node> rootChildren;


 291 
 292     private synchronized void initRootChildren(AbstractJrtPath jrtPath) throws IOException {
 293         if (rootChildren == null) {
 294             rootChildren = new ArrayList<>();
 295             rootChildren.addAll(findNode(jrtPath).getChildren());

 296         }

 297     }
 298 
 299     private Iterator<Path> rootDirIterator(AbstractJrtPath jrtPath) throws IOException {
 300         initRootChildren(jrtPath);
 301         return nodesToIterator(jrtPath, rootChildren);



 302     }
 303 
 304     private List<Node> modulesChildren;
 305 
 306     private synchronized void initModulesChildren(AbstractJrtPath jrtPath) throws IOException {
 307         if (modulesChildren == null) {
 308             modulesChildren = new ArrayList<>();
 309             modulesChildren.addAll(findNode(jrtPath).getChildren());
 310         }


 311     }
 312 
 313     private Iterator<Path> modulesDirIterator(AbstractJrtPath jrtPath) throws IOException {
 314         initModulesChildren(jrtPath);
 315         return nodesToIterator(jrtPath, modulesChildren);



 316     }
 317 
 318     private List<Node> packagesChildren;
 319 
 320     private synchronized void initPackagesChildren(AbstractJrtPath jrtPath) throws IOException {
 321         if (packagesChildren == null) {
 322             packagesChildren = new ArrayList<>();
 323             packagesChildren.addAll(findNode(jrtPath).getChildren());





 324         }










 325     }
 326 
 327     private Iterator<Path> packagesDirIterator(AbstractJrtPath jrtPath) throws IOException {
 328         initPackagesChildren(jrtPath);
 329         return nodesToIterator(jrtPath, packagesChildren);








 330     }
 331 }


   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.internal.jrtfs;
  26 
  27 import java.io.ByteArrayInputStream;
  28 import java.io.IOException;
  29 import java.io.InputStream;
  30 import java.io.OutputStream;
  31 import java.nio.ByteBuffer;
  32 import java.nio.channels.Channels;
  33 import java.nio.channels.FileChannel;
  34 import java.nio.channels.NonWritableChannelException;
  35 import java.nio.channels.ReadableByteChannel;
  36 import java.nio.channels.SeekableByteChannel;
  37 import java.nio.file.ClosedFileSystemException;
  38 import java.nio.file.CopyOption;
  39 import java.nio.file.DirectoryStream;
  40 import java.nio.file.FileStore;
  41 import java.nio.file.FileSystem;
  42 import java.nio.file.FileSystemException;
  43 import java.nio.file.InvalidPathException;
  44 import java.nio.file.LinkOption;
  45 import java.nio.file.NoSuchFileException;
  46 import java.nio.file.NotDirectoryException;
  47 import java.nio.file.OpenOption;
  48 import java.nio.file.Path;
  49 import java.nio.file.PathMatcher;
  50 import java.nio.file.ReadOnlyFileSystemException;
  51 import java.nio.file.StandardOpenOption;
  52 import java.nio.file.WatchService;
  53 import java.nio.file.attribute.FileAttribute;
  54 import java.nio.file.attribute.FileTime;
  55 import java.nio.file.attribute.UserPrincipalLookupService;
  56 import java.nio.file.spi.FileSystemProvider;
  57 import java.util.ArrayList;
  58 import java.util.Arrays;
  59 import java.util.Collections;
  60 import java.util.HashSet;
  61 import java.util.Iterator;

  62 import java.util.Map;
  63 import java.util.Objects;
  64 import java.util.Set;
  65 import java.util.regex.Pattern;
  66 import jdk.internal.jimage.ImageReader.Node;
  67 import static java.util.stream.Collectors.toList;
  68 
  69 /**
  70  * jrt file system implementation built on System jimage files.
  71  *
  72  * @implNote This class needs to maintain JDK 8 source compatibility.
  73  *
  74  * It is used internally in the JDK to implement jimage/jrtfs access,
  75  * but also compiled and delivered as part of the jrtfs.jar to support access
  76  * to the jimage file provided by the shipped JDK by tools running on JDK 8.
  77  */
  78 class JrtFileSystem extends FileSystem {
  79 
  80     private final JrtFileSystemProvider provider;
  81     private final JrtPath rootPath = new JrtPath(this, "/");


  82     private volatile boolean isOpen;
  83     private volatile boolean isClosable;
  84     private SystemImage image;
  85 
  86     JrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> env)
  87             throws IOException
  88     {
  89         this.provider = provider;
  90         this.image = SystemImage.open();  // open image file
  91         this.isOpen = true;
  92         this.isClosable = env != null;












  93     }
  94 
  95     // FileSystem method implementations
  96     @Override
  97     public boolean isOpen() {
  98         return isOpen;
  99     }
 100 
 101     @Override
 102     public void close() throws IOException {
 103         if (!isClosable)
 104             throw new UnsupportedOperationException();
 105         cleanup();
 106     }
 107 
 108     @Override
 109     protected void finalize() throws Throwable {
 110         try {
 111             cleanup();
 112         } catch (IOException ignored) {}
 113     }
 114 
 115     @Override
 116     public FileSystemProvider provider() {
 117         return provider;
 118     }
 119 

 120     @Override
 121     public Iterable<Path> getRootDirectories() {
 122         ArrayList<Path> dirs = new ArrayList<>();
 123         dirs.add(getRootPath());
 124         return dirs;
 125     }
 126 
 127     @Override
 128     public JrtPath getPath(String first, String... more) {
 129         if (more.length == 0) {
 130             return new JrtPath(this, first);
 131         }
 132         StringBuilder sb = new StringBuilder();
 133         sb.append(first);
 134         for (String path : more) {
 135             if (path.length() > 0) {
 136                 if (sb.length() > 0) {
 137                     sb.append('/');
 138                 }
 139                 sb.append(path);
 140             }
 141         }
 142         return new JrtPath(this, sb.toString());
 143     }
 144 
 145     @Override
 146     public final boolean isReadOnly() {
 147         return true;
 148     }
 149 
 150     @Override
 151     public final UserPrincipalLookupService getUserPrincipalLookupService() {
 152         throw new UnsupportedOperationException();



 153     }
 154 
 155     @Override
 156     public final WatchService newWatchService() {
 157         throw new UnsupportedOperationException();
 158     }
 159 
 160     @Override
 161     public final Iterable<FileStore> getFileStores() {
 162         ArrayList<FileStore> list = new ArrayList<>(1);
 163         list.add(getFileStore(getRootPath()));
 164         return list;

 165     }
 166 
 167     private static final Set<String> supportedFileAttributeViews
 168             = Collections.unmodifiableSet(
 169                     new HashSet<String>(Arrays.asList("basic", "jrt")));
 170 
 171     @Override
 172     public final Set<String> supportedFileAttributeViews() {
 173         return supportedFileAttributeViews;
 174     }
 175 
 176     @Override
 177     public final String toString() {
 178         return "jrt:/";



 179     }
 180 
 181     @Override
 182     public final String getSeparator() {
 183         return "/";
 184     }
 185 
 186     @Override
 187     public PathMatcher getPathMatcher(String syntaxAndInput) {
 188         int pos = syntaxAndInput.indexOf(':');
 189         if (pos <= 0 || pos == syntaxAndInput.length()) {
 190             throw new IllegalArgumentException();
 191         }
 192         String syntax = syntaxAndInput.substring(0, pos);
 193         String input = syntaxAndInput.substring(pos + 1);
 194         String expr;
 195         if (syntax.equalsIgnoreCase("glob")) {
 196             expr = JrtUtils.toRegexPattern(input);
 197         } else if (syntax.equalsIgnoreCase("regex")) {
 198             expr = input;
 199         } else {
 200                 throw new UnsupportedOperationException("Syntax '" + syntax
 201                         + "' not recognized");
 202         }
 203         // return matcher
 204         final Pattern pattern = Pattern.compile(expr);
 205         return (Path path) -> pattern.matcher(path.toString()).matches();
 206     }
 207 
 208     JrtPath resolveLink(JrtPath path) throws IOException {
 209         Node node = checkNode(path);
 210         if (node.isLink()) {
 211             node = node.resolveLink();
 212             return new JrtPath(this, node.getName());  // TBD, normalized?
 213         }
 214         return path;
 215     }
 216 
 217     JrtFileAttributes getFileAttributes(JrtPath path, LinkOption... options)
 218             throws IOException {
 219         Node node = checkNode(path);
 220         if (node.isLink() && followLinks(options)) {
 221             return new JrtFileAttributes(node.resolveLink(true));
 222         }
 223         return new JrtFileAttributes(node);
 224     }
 225 
 226     /**
 227      * returns the list of child paths of the given directory "path"
 228      *
 229      * @param path name of the directory whose content is listed
 230      * @return iterator for child paths of the given directory path
 231      */
 232     Iterator<Path> iteratorOf(JrtPath path, DirectoryStream.Filter<? super Path> filter)
 233             throws IOException {
 234         Node node = checkNode(path).resolveLink(true);
 235         if (!node.isDirectory()) {
 236             throw new NotDirectoryException(path.getName());
 237         }
 238         if (filter == null) {
 239             return node.getChildren()
 240                        .stream()
 241                        .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
 242                        .iterator();
 243         }
 244         return node.getChildren()
 245                    .stream()
 246                    .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
 247                    .filter(p ->  { try { return filter.accept(p);
 248                                    } catch (IOException x) {}
 249                                    return false;
 250                                   })
 251                    .iterator();
 252     }
 253 
 254     // returns the content of the file resource specified by the path
 255     byte[] getFileContent(JrtPath path) throws IOException {
 256         Node node = checkNode(path);
 257         if (node.isDirectory()) {
 258             throw new FileSystemException(path + " is a directory");
 259         }
 260         //assert node.isResource() : "resource node expected here";
 261         return image.getResource(node);
 262     }
 263 
 264     /////////////// Implementation details below this point //////////
 265 
 266     // static utility methods
 267     static ReadOnlyFileSystemException readOnly() {
 268         return new ReadOnlyFileSystemException();
 269     }
 270 
 271     // do the supplied options imply that we have to chase symlinks?
 272     static boolean followLinks(LinkOption... options) {
 273         if (options != null) {
 274             for (LinkOption lo : options) {
 275                 Objects.requireNonNull(lo);
 276                 if (lo == LinkOption.NOFOLLOW_LINKS) {
 277                     return false;
 278                 } else {
 279                     throw new AssertionError("should not reach here");
 280                 }
 281             }
 282         }
 283         return true;
 284     }
 285 
 286     // check that the options passed are supported by (read-only) jrt file system
 287     static void checkOptions(Set<? extends OpenOption> options) {
 288         // check for options of null type and option is an intance of StandardOpenOption
 289         for (OpenOption option : options) {
 290             Objects.requireNonNull(option);
 291             if (!(option instanceof StandardOpenOption)) {
 292                 throw new IllegalArgumentException();
 293             }
 294         }
 295         if (options.contains(StandardOpenOption.WRITE) ||
 296             options.contains(StandardOpenOption.APPEND)) {
 297             throw readOnly();
 298         }
 299     }
 300 

 301     // clean up this file system - called from finalize and close
 302     void cleanup() throws IOException {
 303         if (!isOpen) {
 304             return;
 305         }

 306         synchronized (this) {
 307             isOpen = false;
 308             // close image reader and null out
 309             image.close();
 310             image = null;

 311         }
 312     }
 313 
 314     // These methods throw read only file system exception
 315     final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime)
 316             throws IOException {
 317         throw readOnly();


 318     }
 319 
 320     // These methods throw read only file system exception
 321     final void createDirectory(JrtPath jrtPath, FileAttribute<?>... attrs) throws IOException {
 322         throw readOnly();
 323     }
 324 
 325     final void deleteFile(JrtPath jrtPath, boolean failIfNotExists)
 326             throws IOException {
 327         throw readOnly();




 328     }
 329 
 330     final OutputStream newOutputStream(JrtPath jrtPath, OpenOption... options)
 331             throws IOException {
 332         throw readOnly();




 333     }
 334 
 335     final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOption... options)
 336             throws IOException {
 337         throw readOnly();
 338     }
 339 
 340     final FileChannel newFileChannel(JrtPath path,
 341             Set<? extends OpenOption> options,
 342             FileAttribute<?>... attrs)
 343             throws IOException {
 344         throw new UnsupportedOperationException("newFileChannel");
 345     }
 346 
 347     final InputStream newInputStream(JrtPath path) throws IOException {
 348         return new ByteArrayInputStream(getFileContent(path));
 349     }
 350 
 351     final SeekableByteChannel newByteChannel(JrtPath path,
 352             Set<? extends OpenOption> options,
 353             FileAttribute<?>... attrs)
 354             throws IOException {
 355         checkOptions(options);
 356 
 357         byte[] buf = getFileContent(path);
 358         final ReadableByteChannel rbc
 359                 = Channels.newChannel(new ByteArrayInputStream(buf));
 360         final long size = buf.length;
 361         return new SeekableByteChannel() {
 362             long read = 0;
 363 
 364             @Override
 365             public boolean isOpen() {
 366                 return rbc.isOpen();
 367             }
 368 
 369             @Override
 370             public long position() throws IOException {
 371                 return read;
 372             }
 373 
 374             @Override
 375             public SeekableByteChannel position(long pos)
 376                     throws IOException {
 377                 throw new UnsupportedOperationException();
 378             }
 379 
 380             @Override
 381             public int read(ByteBuffer dst) throws IOException {
 382                 int n = rbc.read(dst);
 383                 if (n > 0) {
 384                     read += n;
 385                 }
 386                 return n;
 387             }
 388 
 389             @Override
 390             public SeekableByteChannel truncate(long size)
 391                     throws IOException {
 392                 throw new NonWritableChannelException();
 393             }
 394 
 395             @Override
 396             public int write(ByteBuffer src) throws IOException {
 397                 throw new NonWritableChannelException();
 398             }
 399 
 400             @Override
 401             public long size() throws IOException {
 402                 return size;
 403             }
 404 
 405             @Override
 406             public void close() throws IOException {
 407                 rbc.close();
 408             }
 409         };
 410     }
 411 
 412     final JrtFileStore getFileStore(JrtPath path) {
 413         return new JrtFileStore(path);
 414     }
 415 
 416     final void ensureOpen() throws IOException {
 417         if (!isOpen()) {
 418             throw new ClosedFileSystemException();
 419         }
 420     }
 421 
 422     final JrtPath getRootPath() {
 423         return rootPath;
 424     }
 425 
 426     boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException {
 427         return checkNode(path1) == checkNode(path2);





 428     }
 429 
 430     boolean isLink(JrtPath path) throws IOException {
 431         return checkNode(path).isLink();
 432     }
 433 
 434     boolean exists(JrtPath path) throws IOException {
 435         try {
 436             checkNode(path);
 437         } catch (NoSuchFileException exp) {
 438             return false;
 439         }
 440         return true;
 441     }
 442 
 443     boolean isDirectory(JrtPath path, boolean resolveLinks)
 444             throws IOException {
 445         Node node = checkNode(path);
 446         return resolveLinks && node.isLink()
 447                 ? node.resolveLink(true).isDirectory()
 448                 : node.isDirectory();
 449     }
 450 
 451     JrtPath toRealPath(JrtPath path, LinkOption... options)
 452             throws IOException {
 453         Node node = checkNode(path);
 454         if (followLinks(options) && node.isLink()) {
 455             node = node.resolveLink();

 456         }
 457         // image node holds the real/absolute path name
 458         return new JrtPath(this, node.getName(), true);
 459     }
 460 
 461     private Node lookup(String path) {
 462         try {
 463             return image.findNode(path);
 464         } catch (RuntimeException re) {
 465             throw new InvalidPathException(path, re.toString());
 466         }
 467     }
 468 
 469     private Node lookupSymbolic(String path) {
 470         int i = 1;
 471         while (i < path.length()) {
 472             i = path.indexOf('/', i);
 473             if (i == -1) {
 474                 break;
 475             }
 476             String prefix = path.substring(0, i);
 477             Node node = lookup(prefix);
 478             if (node == null) {
 479                 break;
 480             }
 481             if (node.isLink()) {
 482                 Node link = node.resolveLink(true);
 483                 // resolved symbolic path concatenated to the rest of the path
 484                 String resPath = link.getName() + path.substring(i);
 485                 node = lookup(resPath);
 486                 return node != null ? node : lookupSymbolic(resPath);
 487             }
 488             i++;
 489         }
 490         return null;
 491     }
 492 
 493     Node checkNode(JrtPath path) throws IOException {
 494         ensureOpen();
 495         String p = path.getResolvedPath();
 496         Node node = lookup(p);
 497         if (node == null) {
 498             node = lookupSymbolic(p);
 499             if (node == null) {
 500                 throw new NoSuchFileException(p);
 501             }
 502         }
 503         return node;
 504     }
 505 }