1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 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 import jdk.internal.jimage.UTF8String; 44 45 /** 46 * jrt file system implementation built on System jimage files. 47 */ 48 class JrtFileSystem extends AbstractJrtFileSystem { 49 50 // System image reader 51 private ImageReader bootImage; 52 // root path 53 private final JrtPath rootPath; 54 private volatile boolean isOpen; 55 56 // open a .jimage and build directory structure 57 private static ImageReader openImage(Path path) throws IOException { 58 ImageReader image = ImageReader.open(path.toString()); 59 image.getRootDirectory(); 60 return image; 61 } 62 63 JrtFileSystem(JrtFileSystemProvider provider, 64 Map<String, ?> env) 65 throws IOException { 66 super(provider, env); 67 checkExists(SystemImages.bootImagePath); 68 69 // open image file 70 this.bootImage = openImage(SystemImages.bootImagePath); 71 72 byte[] root = new byte[]{'/'}; 73 rootPath = new JrtPath(this, root); 74 isOpen = true; 75 } 76 77 // FileSystem method implementations 78 @Override 79 public boolean isOpen() { 80 return isOpen; 81 } 82 83 @Override 84 public void close() throws IOException { 85 cleanup(); 86 } 87 88 @Override 89 protected void finalize() throws Throwable { 90 try { 91 cleanup(); 92 } catch (IOException ignored) { 93 } 94 super.finalize(); 95 } 96 97 // AbstractJrtFileSystem method implementations 98 @Override 99 JrtPath getRootPath() { 100 return rootPath; 101 } 102 103 @Override 104 boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException { 105 ensureOpen(); 106 NodeAndImage n1 = findNode(p1.getName()); 107 NodeAndImage n2 = findNode(p2.getName()); 108 return n1.node.equals(n2.node); 109 } 110 111 @Override 112 boolean isLink(AbstractJrtPath jrtPath) throws IOException { 113 return checkNode(jrtPath.getName()).node.isLink(); 114 } 115 116 @Override 117 AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException { 118 NodeAndImage ni = checkNode(jrtPath.getName()); 119 if (ni.node.isLink()) { 120 Node node = ni.node.resolveLink(); 121 return toJrtPath(node.getName().getBytesCopy()); 122 } 123 124 return jrtPath; 125 } 126 127 @Override 128 JrtFileAttributes getFileAttributes(byte[] path, LinkOption... options) 129 throws IOException { 130 NodeAndImage ni = checkNode(path); 131 if (ni.node.isLink() && followLinks(options)) { 132 return new JrtFileAttributes(ni.node.resolveLink(true)); 133 } 134 return new JrtFileAttributes(ni.node); 135 } 136 137 @Override 138 boolean exists(byte[] path) throws IOException { 139 try { 140 checkNode(path); 141 } catch (NoSuchFileException exp) { 142 return false; 143 } 144 return true; 145 } 146 147 @Override 148 boolean isDirectory(byte[] path, boolean resolveLinks) 149 throws IOException { 150 NodeAndImage ni = checkNode(path); 151 return resolveLinks && ni.node.isLink() 152 ? ni.node.resolveLink(true).isDirectory() 153 : ni.node.isDirectory(); 154 } 155 156 @Override 157 Iterator<Path> iteratorOf(byte[] path, String childPrefix) 158 throws IOException { 159 NodeAndImage ni = checkNode(path); 160 Node node = ni.node.resolveLink(true); 161 162 if (!node.isDirectory()) { 163 throw new NotDirectoryException(getString(path)); 164 } 165 166 if (node.isRootDir()) { 167 return rootDirIterator(path, childPrefix); 168 } else if (node.isModulesDir()) { 169 return modulesDirIterator(path, childPrefix); 170 } else if (node.isPackagesDir()) { 171 return packagesDirIterator(path, childPrefix); 172 } 173 174 return nodesToIterator(toJrtPath(path), childPrefix, node.getChildren()); 175 } 176 177 @Override 178 byte[] getFileContent(byte[] path) throws IOException { 179 final NodeAndImage ni = checkResource(path); 180 return ni.getResource(); 181 } 182 183 // Implementation details below this point 184 // clean up this file system - called from finalize and close 185 private void cleanup() throws IOException { 186 if (!isOpen) { 187 return; 188 } 189 190 synchronized (this) { 191 isOpen = false; 192 193 // close all image reader and null out 194 bootImage.close(); 195 bootImage = null; 196 } 197 } 198 199 private static class NodeAndImage { 200 201 final Node node; 202 final ImageReader image; 203 204 NodeAndImage(Node node, ImageReader image) { 205 this.node = node; 206 this.image = image; 207 } 208 209 byte[] getResource() throws IOException { 210 return image.getResource(node); 211 } 212 } 213 214 private NodeAndImage lookup(byte[] path) { 215 ImageReader image = bootImage; 216 Node node; 217 try { 218 node = bootImage.findNode(path); 219 } catch (RuntimeException re) { 220 throw new InvalidPathException(getString(path), re.toString()); 221 } 222 return node != null ? new NodeAndImage(node, image) : null; 223 } 224 225 private NodeAndImage lookupSymbolic(byte[] path) { 226 for (int i = 1; i < path.length; i++) { 227 if (path[i] == (byte) '/') { 228 byte[] prefix = Arrays.copyOfRange(path, 0, i); 229 NodeAndImage ni = lookup(prefix); 230 if (ni == null) { 231 break; 232 } 233 234 if (ni.node.isLink()) { 235 Node link = ni.node.resolveLink(true); 236 // resolved symbolic path concatenated to the rest of the path 237 UTF8String resPath = link.getName().concat(new UTF8String(path, i)); 238 byte[] resPathBytes = resPath.getBytesCopy(); 239 ni = lookup(resPathBytes); 240 return ni != null ? ni : lookupSymbolic(resPathBytes); 241 } 242 } 243 } 244 245 return null; 246 } 247 248 private NodeAndImage findNode(byte[] path) throws IOException { 249 NodeAndImage ni = lookup(path); 250 if (ni == null) { 251 ni = lookupSymbolic(path); 252 if (ni == null) { 253 throw new NoSuchFileException(getString(path)); 254 } 255 } 256 return ni; 257 } 258 259 private NodeAndImage checkNode(byte[] path) throws IOException { 260 ensureOpen(); 261 return findNode(path); 262 } 263 264 private NodeAndImage checkResource(byte[] path) throws IOException { 265 NodeAndImage ni = checkNode(path); 266 if (ni.node.isDirectory()) { 267 throw new FileSystemException(getString(path) + " is a directory"); 268 } 269 270 assert ni.node.isResource() : "resource node expected here"; 271 return ni; 272 } 273 274 private JrtPath toJrtPath(String path) { 275 return toJrtPath(getBytes(path)); 276 } 277 278 private JrtPath toJrtPath(byte[] path) { 279 return new JrtPath(this, path); 280 } 281 282 private Iterator<Path> nodesToIterator(Path path, String childPrefix, List<Node> childNodes) { 283 Function<Node, Path> f = childPrefix == null 284 ? child -> toJrtPath(child.getNameString()) 285 : child -> toJrtPath(childPrefix + child.getNameString().substring(1)); 286 return childNodes.stream().map(f).collect(toList()).iterator(); 287 } 288 289 private List<Node> rootChildren; 290 291 private synchronized void initRootChildren(byte[] path) { 292 if (rootChildren == null) { 293 rootChildren = new ArrayList<>(); 294 rootChildren.addAll(bootImage.findNode(path).getChildren()); 295 } 296 } 297 298 private Iterator<Path> rootDirIterator(byte[] path, String childPrefix) throws IOException { 299 initRootChildren(path); 300 return nodesToIterator(rootPath, childPrefix, rootChildren); 301 } 302 303 private List<Node> modulesChildren; 304 305 private synchronized void initModulesChildren(byte[] path) { 306 if (modulesChildren == null) { 307 modulesChildren = new ArrayList<>(); 308 modulesChildren.addAll(bootImage.findNode(path).getChildren()); 309 } 310 } 311 312 private Iterator<Path> modulesDirIterator(byte[] path, String childPrefix) throws IOException { 313 initModulesChildren(path); 314 return nodesToIterator(new JrtPath(this, path), childPrefix, modulesChildren); 315 } 316 317 private List<Node> packagesChildren; 318 319 private synchronized void initPackagesChildren(byte[] path) { 320 if (packagesChildren == null) { 321 packagesChildren = new ArrayList<>(); 322 packagesChildren.addAll(bootImage.findNode(path).getChildren()); 323 } 324 } 325 326 private Iterator<Path> packagesDirIterator(byte[] path, String childPrefix) throws IOException { 327 initPackagesChildren(path); 328 return nodesToIterator(new JrtPath(this, path), childPrefix, packagesChildren); 329 } 330 }