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 if (!node.isDirectory()) { 162 throw new NotDirectoryException(getString(path)); 163 } 164 165 if (node.isRootDir()) { 166 return rootDirIterator(path, childPrefix); 167 } else if (node.isModulesDir()) { 168 return modulesDirIterator(path, childPrefix); 169 } else if (node.isPackagesDir()) { 170 return packagesDirIterator(path, childPrefix); 171 } 172 173 return nodesToIterator(toJrtPath(path), ni.symLink, childPrefix, node.getChildren()); 174 } 175 176 @Override 177 byte[] getFileContent(byte[] path) throws IOException { 178 final NodeAndImage ni = checkResource(path); 179 return ni.getResource(); 180 } 181 182 // Implementation details below this point 183 // clean up this file system - called from finalize and close 184 private void cleanup() throws IOException { 185 if (!isOpen) { 186 return; 187 } 188 189 synchronized (this) { 190 isOpen = false; 191 192 // close all image reader and null out 193 bootImage.close(); 194 bootImage = null; 195 } 196 } 197 198 private static class NodeAndImage { 199 200 final Node node; 201 final ImageReader image; 202 // was there a symlink somewhere while resolving this Node from name? 203 final boolean symLink; 204 205 NodeAndImage(Node node, ImageReader image, boolean symLink) { 206 this.node = node; 207 this.image = image; 208 this.symLink = symLink; 209 } 210 211 byte[] getResource() throws IOException { 212 return image.getResource(node); 213 } 214 } 215 216 private NodeAndImage lookup(byte[] path) { 217 return lookup(path, false); 218 } 219 220 private NodeAndImage lookup(byte[] path, boolean symLink) { 221 ImageReader image = bootImage; 222 Node node; 223 try { 224 node = bootImage.findNode(path); 225 } catch (RuntimeException re) { 226 throw new InvalidPathException(getString(path), re.toString()); 227 } 228 return node != null ? new NodeAndImage(node, image, symLink) : null; 229 } 230 231 private NodeAndImage lookupSymbolic(byte[] path) { 232 for (int i = 1; i < path.length; i++) { 233 if (path[i] == (byte) '/') { 234 byte[] prefix = Arrays.copyOfRange(path, 0, i); 235 NodeAndImage ni = lookup(prefix, true); 236 if (ni == null) { 237 break; 238 } 239 240 if (ni.node.isLink()) { 241 Node link = ni.node.resolveLink(true); 242 // resolved symbolic path concatenated to the rest of the path 243 UTF8String resPath = link.getName().concat(new UTF8String(path, i)); 244 byte[] resPathBytes = resPath.getBytesCopy(); 245 ni = lookup(resPathBytes, true); 246 return ni != null ? ni : lookupSymbolic(resPathBytes); 247 } 248 } 249 } 250 251 return null; 252 } 253 254 private NodeAndImage findNode(byte[] path) throws IOException { 255 NodeAndImage ni = lookup(path); 256 if (ni == null) { 257 ni = lookupSymbolic(path); 258 if (ni == null) { 259 throw new NoSuchFileException(getString(path)); 260 } 261 } 262 return ni; 263 } 264 265 private NodeAndImage checkNode(byte[] path) throws IOException { 266 ensureOpen(); 267 return findNode(path); 268 } 269 270 private NodeAndImage checkResource(byte[] path) throws IOException { 271 NodeAndImage ni = checkNode(path); 272 if (ni.node.isDirectory()) { 273 throw new FileSystemException(getString(path) + " is a directory"); 274 } 275 276 assert ni.node.isResource() : "resource node expected here"; 277 return ni; 278 } 279 280 private JrtPath toJrtPath(String path) { 281 return toJrtPath(getBytes(path)); 282 } 283 284 private JrtPath toJrtPath(byte[] path) { 285 return new JrtPath(this, path); 286 } 287 288 private static final int MODULES_LEN = "/modules/".length(); 289 private static final int PACKAGES_LEN = "/packages/".length(); 290 291 private Iterator<Path> nodesToIterator(Path path, boolean symLink, String childPrefix, List<Node> childNodes) { 292 // Function that maps name -> Path (adding prefix if needed) 293 Function<String, Path> prefixHandler = childPrefix == null 294 ? childStr -> toJrtPath(childStr) 295 : childStr -> toJrtPath(childPrefix + childStr.substring(1)); 296 297 // function to map Node->name 298 Function<Node, String> nodeHandler; 299 if (symLink) { 300 // There was a symlink in the parent dir! We need to make sure that the child paths 301 // use the appropriate parent prefix in the name. 302 String parentStr = path.toAbsolutePath().toString(); 303 // "/packages/java.lang/java.base/java/" -> "/packages/java.lang/" 304 String parentPrefix = parentStr.substring(0, 1 + parentStr.indexOf('/', PACKAGES_LEN)); 305 306 nodeHandler = node -> parentPrefix + node.getNameString().substring(MODULES_LEN); 307 } else { 308 nodeHandler = Node::getNameString; 309 } 310 311 return childNodes.stream().map(nodeHandler.andThen(prefixHandler)).collect(toList()).iterator(); 312 } 313 314 private List<Node> rootChildren; 315 316 private synchronized void initRootChildren(byte[] path) { 317 if (rootChildren == null) { 318 rootChildren = new ArrayList<>(); 319 rootChildren.addAll(bootImage.findNode(path).getChildren()); 320 } 321 } 322 323 private Iterator<Path> rootDirIterator(byte[] path, String childPrefix) throws IOException { 324 initRootChildren(path); 325 return nodesToIterator(rootPath, false, childPrefix, rootChildren); 326 } 327 328 private List<Node> modulesChildren; 329 330 private synchronized void initModulesChildren(byte[] path) { 331 if (modulesChildren == null) { 332 modulesChildren = new ArrayList<>(); 333 modulesChildren.addAll(bootImage.findNode(path).getChildren()); 334 } 335 } 336 337 private Iterator<Path> modulesDirIterator(byte[] path, String childPrefix) throws IOException { 338 initModulesChildren(path); 339 return nodesToIterator(new JrtPath(this, path), false, childPrefix, modulesChildren); 340 } 341 342 private List<Node> packagesChildren; 343 344 private synchronized void initPackagesChildren(byte[] path) { 345 if (packagesChildren == null) { 346 packagesChildren = new ArrayList<>(); 347 packagesChildren.addAll(bootImage.findNode(path).getChildren()); 348 } 349 } 350 351 private Iterator<Path> packagesDirIterator(byte[] path, String childPrefix) throws IOException { 352 initPackagesChildren(path); 353 return nodesToIterator(new JrtPath(this, path), false, childPrefix, packagesChildren); 354 } 355 } --- EOF ---