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.*; 33 import java.nio.charset.Charset; 34 import java.nio.file.AccessMode; 35 import java.nio.file.ClosedFileSystemException; 36 import java.nio.file.CopyOption; 37 import java.nio.file.FileStore; 38 import java.nio.file.FileSystem; 39 import java.nio.file.FileSystemException; 40 import java.nio.file.FileSystemNotFoundException; 41 import java.nio.file.Files; 42 import java.nio.file.NoSuchFileException; 43 import java.nio.file.NotDirectoryException; 44 import java.nio.file.OpenOption; 45 import java.nio.file.Path; 46 import java.nio.file.PathMatcher; 47 import java.nio.file.ReadOnlyFileSystemException; 48 import java.nio.file.StandardCopyOption; 49 import java.nio.file.StandardOpenOption; 50 import java.nio.file.WatchService; 51 import java.nio.file.attribute.FileAttribute; 52 import java.nio.file.attribute.FileTime; 53 import java.nio.file.attribute.UserPrincipalLookupService; 54 import java.nio.file.spi.FileSystemProvider; 55 import java.security.AccessController; 56 import java.security.PrivilegedActionException; 57 import java.security.PrivilegedExceptionAction; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.Collections; 61 import java.util.HashSet; 62 import java.util.Iterator; 63 import java.util.List; 64 import java.util.Map; 65 import java.util.Set; 66 import java.util.regex.Pattern; 67 import java.util.stream.Collectors; 68 import jdk.internal.jimage.ImageReader; 69 import jdk.internal.jimage.ImageReader.Node; 70 import jdk.internal.jimage.UTF8String; 71 72 /** 73 * A FileSystem built on System jimage files. 74 */ 75 class JrtFileSystem extends FileSystem { 76 private static final Charset UTF_8 = Charset.forName("UTF-8"); 77 private final JrtFileSystemProvider provider; 78 // System image readers 79 private ImageReader bootImage; 80 private ImageReader extImage; 81 private ImageReader appImage; 82 // root path 83 private final JrtPath rootPath; 84 private volatile boolean isOpen; 85 86 private static void checkExists(Path path) { 87 if (Files.notExists(path)) { 88 throw new FileSystemNotFoundException(path.toString()); 89 } 90 } 91 92 // open a .jimage and build directory structure 93 private static ImageReader openImage(Path path) throws IOException { 94 ImageReader image = ImageReader.open(path.toString()); 95 image.getRootDirectory(); 96 return image; 97 } 98 99 JrtFileSystem(JrtFileSystemProvider provider, 100 Map<String, ?> env) 101 throws IOException { 102 this.provider = provider; 103 checkExists(SystemImages.bootImagePath); 104 checkExists(SystemImages.extImagePath); 105 checkExists(SystemImages.appImagePath); 106 107 // open image files 108 this.bootImage = openImage(SystemImages.bootImagePath); 109 this.extImage = openImage(SystemImages.extImagePath); 110 this.appImage = openImage(SystemImages.appImagePath); 111 112 rootPath = new JrtPath(this, new byte[]{'/'}); 113 isOpen = true; 114 } 115 116 @Override 117 public FileSystemProvider provider() { 118 return provider; 119 } 120 121 @Override 122 public String getSeparator() { 123 return "/"; 124 } 125 126 @Override 127 public boolean isOpen() { 128 return isOpen; 129 } 130 131 @Override 132 public void close() throws IOException { 133 cleanup(); 134 } 135 136 @Override 137 protected void finalize() { 138 try { 139 cleanup(); 140 } catch (IOException ignored) {} 141 } 142 143 // clean up this file system - called from finalize and close 144 private void cleanup() throws IOException { 145 if (!isOpen) { 146 return; 147 } 148 149 synchronized(this) { 150 isOpen = false; 151 152 // close all image readers and null out 153 bootImage.close(); 154 extImage.close(); 155 appImage.close(); 156 bootImage = null; 157 extImage = null; 158 appImage = null; 159 } 160 } 161 162 private void ensureOpen() throws IOException { 163 if (!isOpen) { 164 throw new ClosedFileSystemException(); 165 } 166 } 167 168 @Override 169 public boolean isReadOnly() { 170 return true; 171 } 172 173 private ReadOnlyFileSystemException readOnly() { 174 return new ReadOnlyFileSystemException(); 175 } 176 177 @Override 272 return name.getBytes(UTF_8); 273 } 274 275 static String getString(byte[] name) { 276 return new String(name, UTF_8); 277 } 278 279 private static class NodeAndImage { 280 final Node node; 281 final ImageReader image; 282 283 NodeAndImage(Node node, ImageReader image) { 284 this.node = node; this.image = image; 285 } 286 287 byte[] getResource() throws IOException { 288 return image.getResource(node); 289 } 290 } 291 292 private NodeAndImage findNode(byte[] path) throws IOException { 293 ImageReader image = bootImage; 294 Node node = bootImage.findNode(path); 295 if (node == null) { 296 image = extImage; 297 node = extImage.findNode(path); 298 } 299 if (node == null) { 300 image = appImage; 301 node = appImage.findNode(path); 302 } 303 if (node == null || node.isHidden()) { 304 throw new NoSuchFileException(getString(path)); 305 } 306 return new NodeAndImage(node, image); 307 } 308 309 private NodeAndImage checkNode(byte[] path) throws IOException { 310 ensureOpen(); 311 return findNode(path); 312 } 313 314 private NodeAndImage checkResource(byte[] path) throws IOException { 315 NodeAndImage ni = checkNode(path); 316 if (ni.node.isDirectory()) { 317 throw new FileSystemException(getString(path) + " is a directory"); 318 } 319 320 assert ni.node.isResource() : "resource node expected here"; 321 return ni; 322 } 323 324 // package private helpers 325 JrtFileAttributes getFileAttributes(byte[] path) 326 throws IOException { 327 NodeAndImage ni = checkNode(path); 328 return new JrtFileAttributes(ni.node); 329 } 330 331 void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime) 332 throws IOException { 333 throw readOnly(); 334 } 335 336 boolean exists(byte[] path) throws IOException { 337 ensureOpen(); 338 try { 339 findNode(path); 340 } catch (NoSuchFileException exp) { 341 return false; 342 } 343 return true; 344 } 345 346 boolean isDirectory(byte[] path) 347 throws IOException { 348 ensureOpen(); 349 NodeAndImage ni = checkNode(path); 350 return ni.node.isDirectory(); 351 } 352 353 JrtPath toJrtPath(String path) { 354 return toJrtPath(getBytes(path)); 355 } 356 357 JrtPath toJrtPath(byte[] path) { 358 return new JrtPath(this, path); 359 } 360 361 /** 362 * returns the list of child paths of the given directory "path" 363 * 364 * @param path name of the directory whose content is listed 365 * @param childPrefix prefix added to returned children names - may be null 366 in which case absolute child paths are returned 367 * @return iterator for child paths of the given directory path 368 */ 369 Iterator<Path> iteratorOf(byte[] path, String childPrefix) 370 throws IOException { 371 NodeAndImage ni = checkNode(path); 372 if (!ni.node.isDirectory()) { 373 throw new NotDirectoryException(getString(path)); 374 } 375 376 if (ni.node.isRootDir()) { 377 return rootDirIterator(path, childPrefix); 378 } 379 380 return nodesToIterator(toJrtPath(path), childPrefix, ni.node.getChildren()); 381 } 382 383 private Iterator<Path> nodesToIterator(Path path, String childPrefix, List<Node> childNodes) { 384 List<Path> childPaths; 385 if (childPrefix == null) { 386 childPaths = childNodes.stream() 387 .filter(Node::isVisible) 388 .map(child -> toJrtPath(child.getNameString())) 389 .collect(Collectors.toCollection(ArrayList::new)); 390 } else { 391 childPaths = childNodes.stream() 392 .filter(Node::isVisible) 393 .map(child -> toJrtPath(childPrefix + child.getNameString().substring(1))) 394 .collect(Collectors.toCollection(ArrayList::new)); 395 } 396 return childPaths.iterator(); 397 } 398 399 private List<Node> rootChildren; 400 private static void addRootDirContent(List<Node> dest, List<Node> src) { 401 for (Node n : src) { 402 // only module directories at the top level. Filter other stuff! 403 if (n.isModuleDir()) { 404 dest.add(n); 405 } 406 } 407 } 408 409 private synchronized void initRootChildren(byte[] path) { 410 if (rootChildren == null) { 411 rootChildren = new ArrayList<>(); 412 addRootDirContent(rootChildren, bootImage.findNode(path).getChildren()); 413 addRootDirContent(rootChildren, extImage.findNode(path).getChildren()); 414 addRootDirContent(rootChildren, appImage.findNode(path).getChildren()); 415 } 416 } 417 418 private Iterator<Path> rootDirIterator(byte[] path, String childPrefix) throws IOException { 419 initRootChildren(path); 420 return nodesToIterator(rootPath, childPrefix, rootChildren); 421 } 422 423 void createDirectory(byte[] dir, FileAttribute<?>... attrs) 424 throws IOException { 425 throw readOnly(); 426 } 427 428 void copyFile(boolean deletesrc, byte[] src, byte[] dst, CopyOption... options) 429 throws IOException { 430 throw readOnly(); 431 } 432 433 public void deleteFile(byte[] path, boolean failIfNotExists) 434 throws IOException { 435 throw readOnly(); 436 } 437 438 OutputStream newOutputStream(byte[] path, OpenOption... options) 439 throws IOException { 440 throw readOnly(); 441 } 442 | 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.*; 33 import java.nio.charset.Charset; 34 import java.nio.file.ClosedFileSystemException; 35 import java.nio.file.CopyOption; 36 import java.nio.file.LinkOption; 37 import java.nio.file.FileStore; 38 import java.nio.file.FileSystem; 39 import java.nio.file.FileSystemException; 40 import java.nio.file.FileSystemNotFoundException; 41 import java.nio.file.Files; 42 import java.nio.file.NoSuchFileException; 43 import java.nio.file.NotDirectoryException; 44 import java.nio.file.OpenOption; 45 import java.nio.file.Path; 46 import java.nio.file.PathMatcher; 47 import java.nio.file.ReadOnlyFileSystemException; 48 import java.nio.file.StandardOpenOption; 49 import java.nio.file.WatchService; 50 import java.nio.file.attribute.FileAttribute; 51 import java.nio.file.attribute.FileTime; 52 import java.nio.file.attribute.UserPrincipalLookupService; 53 import java.nio.file.spi.FileSystemProvider; 54 import java.util.concurrent.ConcurrentHashMap; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.Collections; 58 import java.util.HashSet; 59 import java.util.Iterator; 60 import java.util.List; 61 import java.util.Map; 62 import java.util.Set; 63 import java.util.function.Function; 64 import java.util.regex.Pattern; 65 import static java.util.stream.Collectors.toList; 66 import jdk.internal.jimage.ImageReader; 67 import jdk.internal.jimage.ImageReader.Node; 68 import jdk.internal.jimage.UTF8String; 69 70 /** 71 * A FileSystem built on System jimage files. 72 */ 73 class JrtFileSystem extends FileSystem { 74 private static final Charset UTF_8 = Charset.forName("UTF-8"); 75 76 private final JrtFileSystemProvider provider; 77 // System image readers 78 private ImageReader bootImage; 79 private ImageReader extImage; 80 private ImageReader appImage; 81 // root path 82 private final JrtPath rootPath; 83 private volatile boolean isOpen; 84 85 private static void checkExists(Path path) { 86 if (Files.notExists(path)) { 87 throw new FileSystemNotFoundException(path.toString()); 88 } 89 } 90 91 // open a .jimage and build directory structure 92 private static ImageReader openImage(Path path) throws IOException { 93 ImageReader image = ImageReader.open(path.toString()); 94 image.getRootDirectory(); 95 return image; 96 } 97 98 JrtFileSystem(JrtFileSystemProvider provider, 99 Map<String, ?> env) 100 throws IOException { 101 this.provider = provider; 102 checkExists(SystemImages.bootImagePath); 103 checkExists(SystemImages.extImagePath); 104 checkExists(SystemImages.appImagePath); 105 106 // open image files 107 this.bootImage = openImage(SystemImages.bootImagePath); 108 this.extImage = openImage(SystemImages.extImagePath); 109 this.appImage = openImage(SystemImages.appImagePath); 110 111 byte[] root = new byte[] { '/' }; 112 rootPath = new JrtPath(this, root); 113 isOpen = true; 114 } 115 116 @Override 117 public FileSystemProvider provider() { 118 return provider; 119 } 120 121 @Override 122 public String getSeparator() { 123 return "/"; 124 } 125 126 @Override 127 public boolean isOpen() { 128 return isOpen; 129 } 130 131 @Override 132 public void close() throws IOException { 133 cleanup(); 134 } 135 136 @Override 137 protected void finalize() { 138 try { 139 cleanup(); 140 } catch (IOException ignored) {} 141 } 142 143 // clean up this file system - called from finalize and close 144 private void cleanup() throws IOException { 145 if (!isOpen) { 146 return; 147 } 148 149 synchronized(this) { 150 isOpen = false; 151 152 // close all image reader and null out 153 bootImage.close(); 154 bootImage = null; 155 extImage.close(); 156 extImage = null; 157 appImage.close(); 158 appImage = null; 159 } 160 } 161 162 private void ensureOpen() throws IOException { 163 if (!isOpen) { 164 throw new ClosedFileSystemException(); 165 } 166 } 167 168 @Override 169 public boolean isReadOnly() { 170 return true; 171 } 172 173 private ReadOnlyFileSystemException readOnly() { 174 return new ReadOnlyFileSystemException(); 175 } 176 177 @Override 272 return name.getBytes(UTF_8); 273 } 274 275 static String getString(byte[] name) { 276 return new String(name, UTF_8); 277 } 278 279 private static class NodeAndImage { 280 final Node node; 281 final ImageReader image; 282 283 NodeAndImage(Node node, ImageReader image) { 284 this.node = node; this.image = image; 285 } 286 287 byte[] getResource() throws IOException { 288 return image.getResource(node); 289 } 290 } 291 292 private NodeAndImage lookup(byte[] path) { 293 Node node = bootImage.findNode(path); 294 ImageReader image = bootImage; 295 if (node == null) { 296 node = extImage.findNode(path); 297 image = extImage; 298 } 299 if (node == null) { 300 node = appImage.findNode(path); 301 image = appImage; 302 } 303 return node != null? new NodeAndImage(node, image) : null; 304 } 305 306 private NodeAndImage lookupSymbolic(byte[] path) { 307 for (int i = 1; i < path.length; i++) { 308 if (path[i] == (byte)'/') { 309 byte[] prefix = Arrays.copyOfRange(path, 0, i); 310 NodeAndImage ni = lookup(prefix); 311 if (ni == null) { 312 break; 313 } 314 315 if (ni.node.isLink()) { 316 Node link = ni.node.resolveLink(true); 317 // resolved symbolic path concatenated to the rest of the path 318 UTF8String resPath = link.getName().concat(new UTF8String(path, i)); 319 byte[] resPathBytes = resPath.getBytesCopy(); 320 ni = lookup(resPathBytes); 321 return ni != null? ni : lookupSymbolic(resPathBytes); 322 } 323 } 324 } 325 326 return null; 327 } 328 329 private NodeAndImage findNode(byte[] path) throws IOException { 330 NodeAndImage ni = lookup(path); 331 if (ni == null) { 332 ni = lookupSymbolic(path); 333 if (ni == null) { 334 throw new NoSuchFileException(getString(path)); 335 } 336 } 337 return ni; 338 } 339 340 private NodeAndImage checkNode(byte[] path) throws IOException { 341 ensureOpen(); 342 return findNode(path); 343 } 344 345 private NodeAndImage checkResource(byte[] path) throws IOException { 346 NodeAndImage ni = checkNode(path); 347 if (ni.node.isDirectory()) { 348 throw new FileSystemException(getString(path) + " is a directory"); 349 } 350 351 assert ni.node.isResource() : "resource node expected here"; 352 return ni; 353 } 354 355 static boolean followLinks(LinkOption... options) { 356 if (options != null) { 357 for (LinkOption lo : options) { 358 if (lo == LinkOption.NOFOLLOW_LINKS) { 359 return false; 360 } else if (lo == null) { 361 throw new NullPointerException(); 362 } else { 363 throw new AssertionError("should not reach here"); 364 } 365 } 366 } 367 return true; 368 } 369 370 // package private helpers 371 JrtFileAttributes getFileAttributes(byte[] path, LinkOption... options) 372 throws IOException { 373 NodeAndImage ni = checkNode(path); 374 if (ni.node.isLink() && followLinks(options)) { 375 return new JrtFileAttributes(ni.node.resolveLink(true)); 376 } 377 return new JrtFileAttributes(ni.node); 378 } 379 380 void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime) 381 throws IOException { 382 throw readOnly(); 383 } 384 385 boolean exists(byte[] path) throws IOException { 386 ensureOpen(); 387 try { 388 findNode(path); 389 } catch (NoSuchFileException exp) { 390 return false; 391 } 392 return true; 393 } 394 395 boolean isDirectory(byte[] path, boolean resolveLinks) 396 throws IOException { 397 ensureOpen(); 398 NodeAndImage ni = checkNode(path); 399 return resolveLinks && ni.node.isLink()? 400 ni.node.resolveLink(true).isDirectory() : 401 ni.node.isDirectory(); 402 } 403 404 JrtPath toJrtPath(String path) { 405 return toJrtPath(getBytes(path)); 406 } 407 408 JrtPath toJrtPath(byte[] path) { 409 return new JrtPath(this, path); 410 } 411 412 boolean isSameFile(JrtPath p1, JrtPath p2) throws IOException { 413 NodeAndImage n1 = findNode(p1.getName()); 414 NodeAndImage n2 = findNode(p2.getName()); 415 return n1.node.equals(n2.node); 416 } 417 418 boolean isLink(JrtPath jrtPath) throws IOException { 419 return findNode(jrtPath.getName()).node.isLink(); 420 } 421 422 JrtPath resolveLink(JrtPath jrtPath) throws IOException { 423 NodeAndImage ni = findNode(jrtPath.getName()); 424 if (ni.node.isLink()) { 425 Node node = ni.node.resolveLink(); 426 return toJrtPath(node.getName().getBytesCopy()); 427 } 428 429 return jrtPath; 430 } 431 432 private Map<UTF8String, List<Node>> packagesTreeChildren = new ConcurrentHashMap<>(); 433 434 /** 435 * returns the list of child paths of the given directory "path" 436 * 437 * @param path name of the directory whose content is listed 438 * @param childPrefix prefix added to returned children names - may be null 439 in which case absolute child paths are returned 440 * @return iterator for child paths of the given directory path 441 */ 442 Iterator<Path> iteratorOf(byte[] path, String childPrefix) 443 throws IOException { 444 NodeAndImage ni = checkNode(path); 445 Node node = ni.node.resolveLink(true); 446 447 if (!node.isDirectory()) { 448 throw new NotDirectoryException(getString(path)); 449 } 450 451 if (node.isRootDir()) { 452 return rootDirIterator(path, childPrefix); 453 } else if (node.isModulesDir()) { 454 return modulesDirIterator(path, childPrefix); 455 } else if (node.isPackagesDir()) { 456 return packagesDirIterator(path, childPrefix); 457 } else if (node.getNameString().startsWith("/packages/")) { 458 if (ni.image != appImage) { 459 UTF8String name = node.getName(); 460 List<Node> children = packagesTreeChildren.get(name); 461 if (children != null) { 462 return nodesToIterator(toJrtPath(path), childPrefix, children); 463 } 464 465 children = new ArrayList<>(); 466 children.addAll(node.getChildren()); 467 Node tmpNode = null; 468 // found in boot 469 if (ni.image == bootImage) { 470 tmpNode = extImage.findNode(name); 471 if (tmpNode != null) { 472 children.addAll(tmpNode.getChildren()); 473 } 474 } 475 476 // found in ext 477 tmpNode = appImage.findNode(name); 478 if (tmpNode != null) { 479 children.addAll(tmpNode.getChildren()); 480 } 481 482 packagesTreeChildren.put(name, children); 483 return nodesToIterator(toJrtPath(path), childPrefix, children); 484 } 485 } 486 487 return nodesToIterator(toJrtPath(path), childPrefix, node.getChildren()); 488 } 489 490 private Iterator<Path> nodesToIterator(Path path, String childPrefix, List<Node> childNodes) { 491 Function<Node, Path> f = childPrefix == null 492 ? child -> toJrtPath(child.getNameString()) 493 : child -> toJrtPath(childPrefix + child.getNameString().substring(1)); 494 return childNodes.stream().map(f).collect(toList()).iterator(); 495 } 496 497 private void addRootDirContent(List<Node> children) { 498 for (Node child : children) { 499 if (!(child.isModulesDir() || child.isPackagesDir())) { 500 rootChildren.add(child); 501 } 502 } 503 } 504 505 private List<Node> rootChildren; 506 private synchronized void initRootChildren(byte[] path) { 507 if (rootChildren == null) { 508 rootChildren = new ArrayList<>(); 509 rootChildren.addAll(bootImage.findNode(path).getChildren()); 510 addRootDirContent(extImage.findNode(path).getChildren()); 511 addRootDirContent(appImage.findNode(path).getChildren()); 512 } 513 } 514 515 private Iterator<Path> rootDirIterator(byte[] path, String childPrefix) throws IOException { 516 initRootChildren(path); 517 return nodesToIterator(rootPath, childPrefix, rootChildren); 518 } 519 520 private List<Node> modulesChildren; 521 private synchronized void initModulesChildren(byte[] path) { 522 if (modulesChildren == null) { 523 modulesChildren = new ArrayList<>(); 524 modulesChildren.addAll(bootImage.findNode(path).getChildren()); 525 modulesChildren.addAll(appImage.findNode(path).getChildren()); 526 modulesChildren.addAll(extImage.findNode(path).getChildren()); 527 } 528 } 529 530 private Iterator<Path> modulesDirIterator(byte[] path, String childPrefix) throws IOException { 531 initModulesChildren(path); 532 return nodesToIterator(new JrtPath(this, path), childPrefix, modulesChildren); 533 } 534 535 private List<Node> packagesChildren; 536 private synchronized void initPackagesChildren(byte[] path) { 537 if (packagesChildren == null) { 538 packagesChildren = new ArrayList<>(); 539 packagesChildren.addAll(bootImage.findNode(path).getChildren()); 540 packagesChildren.addAll(extImage.findNode(path).getChildren()); 541 packagesChildren.addAll(appImage.findNode(path).getChildren()); 542 } 543 } 544 private Iterator<Path> packagesDirIterator(byte[] path, String childPrefix) throws IOException { 545 initPackagesChildren(path); 546 return nodesToIterator(new JrtPath(this, path), childPrefix, packagesChildren); 547 } 548 549 void createDirectory(byte[] dir, FileAttribute<?>... attrs) 550 throws IOException { 551 throw readOnly(); 552 } 553 554 void copyFile(boolean deletesrc, byte[] src, byte[] dst, CopyOption... options) 555 throws IOException { 556 throw readOnly(); 557 } 558 559 public void deleteFile(byte[] path, boolean failIfNotExists) 560 throws IOException { 561 throw readOnly(); 562 } 563 564 OutputStream newOutputStream(byte[] path, OpenOption... options) 565 throws IOException { 566 throw readOnly(); 567 } 568 |