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 } |