1 /* 2 * Copyright (c) 2014, 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 26 package jdk.internal.jrtfs; 27 28 import java.io.*; 29 import java.net.URI; 30 import java.net.URISyntaxException; 31 import java.nio.channels.*; 32 import java.nio.file.*; 33 import java.nio.file.DirectoryStream.Filter; 34 import java.nio.file.attribute.*; 35 import java.util.*; 36 import static java.nio.file.StandardOpenOption.*; 37 import static java.nio.file.StandardCopyOption.*; 38 39 final class JrtPath implements Path { 40 41 private final JrtFileSystem jrtfs; 42 private final byte[] path; 43 private volatile int[] offsets; 44 private int hashcode = 0; // cached hashcode (created lazily) 45 46 JrtPath(JrtFileSystem jrtfs, byte[] path) { 47 this(jrtfs, path, false); 48 } 49 50 JrtPath(JrtFileSystem jrtfs, byte[] path, boolean normalized) { 51 this.jrtfs = jrtfs; 52 if (normalized) 53 this.path = path; 54 else 55 this.path = normalize(path); 56 } 57 58 byte[] getName() { 59 return path; 60 } 61 62 @Override 63 public JrtPath getRoot() { 64 if (this.isAbsolute()) 65 return jrtfs.getRootPath(); 66 else 67 return null; 68 } 69 70 @Override 71 public Path getFileName() { 72 initOffsets(); 73 int count = offsets.length; 74 if (count == 0) 75 return null; // no elements so no name 76 if (count == 1 && path[0] != '/') 77 return this; 78 int lastOffset = offsets[count-1]; 79 int len = path.length - lastOffset; 80 byte[] result = new byte[len]; 81 System.arraycopy(path, lastOffset, result, 0, len); 82 return new JrtPath(jrtfs, result); 83 } 84 85 @Override 86 public JrtPath getParent() { 87 initOffsets(); 88 int count = offsets.length; 89 if (count == 0) // no elements so no parent 90 return null; 91 int len = offsets[count-1] - 1; 92 if (len <= 0) // parent is root only (may be null) 93 return getRoot(); 94 byte[] result = new byte[len]; 95 System.arraycopy(path, 0, result, 0, len); 96 return new JrtPath(jrtfs, result); 97 } 98 99 @Override 100 public int getNameCount() { 101 initOffsets(); 102 return offsets.length; 103 } 104 105 @Override 106 public JrtPath getName(int index) { 107 initOffsets(); 108 if (index < 0 || index >= offsets.length) 109 throw new IllegalArgumentException(); 110 int begin = offsets[index]; 111 int len; 112 if (index == (offsets.length-1)) 113 len = path.length - begin; 114 else 115 len = offsets[index+1] - begin - 1; 116 // construct result 117 byte[] result = new byte[len]; 118 System.arraycopy(path, begin, result, 0, len); 119 return new JrtPath(jrtfs, result); 120 } 121 122 @Override 123 public JrtPath subpath(int beginIndex, int endIndex) { 124 initOffsets(); 125 if (beginIndex < 0 || 126 beginIndex >= offsets.length || 127 endIndex > offsets.length || 128 beginIndex >= endIndex) 129 throw new IllegalArgumentException(); 130 131 // starting offset and length 132 int begin = offsets[beginIndex]; 133 int len; 134 if (endIndex == offsets.length) 135 len = path.length - begin; 136 else 137 len = offsets[endIndex] - begin - 1; 138 // construct result 139 byte[] result = new byte[len]; 140 System.arraycopy(path, begin, result, 0, len); 141 return new JrtPath(jrtfs, result); 142 } 143 144 @Override 145 public JrtPath toRealPath(LinkOption... options) throws IOException { 146 JrtPath realPath = new JrtPath(jrtfs, getResolvedPath()).toAbsolutePath(); 147 realPath = JrtFileSystem.followLinks(options)? jrtfs.resolveLink(this) : realPath; 148 realPath.checkAccess(); 149 return realPath; 150 } 151 152 JrtPath readSymbolicLink() throws IOException { 153 if (! jrtfs.isLink(this)) { 154 throw new IOException("not a symbolic link"); 155 } 156 157 return jrtfs.resolveLink(this); 158 } 159 160 boolean isHidden() { 161 return false; 162 } 163 164 @Override 165 public JrtPath toAbsolutePath() { 166 if (isAbsolute()) { 167 return this; 168 } else { 169 //add / bofore the existing path 170 byte[] tmp = new byte[path.length + 1]; 171 tmp[0] = '/'; 172 System.arraycopy(path, 0, tmp, 1, path.length); 173 return (JrtPath) new JrtPath(jrtfs, tmp).normalize(); 174 } 175 } 176 177 @Override 178 public URI toUri() { 179 try { 180 return new URI("jrt", 181 JrtFileSystem.getString(toAbsolutePath().path), 182 null); 183 } catch (URISyntaxException ex) { 184 throw new AssertionError(ex); 185 } 186 } 187 188 private boolean equalsNameAt(JrtPath other, int index) { 189 int mbegin = offsets[index]; 190 int mlen; 191 if (index == (offsets.length-1)) 192 mlen = path.length - mbegin; 193 else 194 mlen = offsets[index + 1] - mbegin - 1; 195 int obegin = other.offsets[index]; 196 int olen; 197 if (index == (other.offsets.length - 1)) 198 olen = other.path.length - obegin; 199 else 200 olen = other.offsets[index + 1] - obegin - 1; 201 if (mlen != olen) 202 return false; 203 int n = 0; 204 while(n < mlen) { 205 if (path[mbegin + n] != other.path[obegin + n]) 206 return false; 207 n++; 208 } 209 return true; 210 } 211 212 @Override 213 public Path relativize(Path other) { 214 final JrtPath o = checkPath(other); 215 if (o.equals(this)) 216 return new JrtPath(getFileSystem(), new byte[0], true); 217 if (/* this.getFileSystem() != o.getFileSystem() || */ 218 this.isAbsolute() != o.isAbsolute()) { 219 throw new IllegalArgumentException(); 220 } 221 int mc = this.getNameCount(); 222 int oc = o.getNameCount(); 223 int n = Math.min(mc, oc); 224 int i = 0; 225 while (i < n) { 226 if (!equalsNameAt(o, i)) 227 break; 228 i++; 229 } 230 int dotdots = mc - i; 231 int len = dotdots * 3 - 1; 232 if (i < oc) 233 len += (o.path.length - o.offsets[i] + 1); 234 byte[] result = new byte[len]; 235 236 int pos = 0; 237 while (dotdots > 0) { 238 result[pos++] = (byte)'.'; 239 result[pos++] = (byte)'.'; 240 if (pos < len) // no tailing slash at the end 241 result[pos++] = (byte)'/'; 242 dotdots--; 243 } 244 if (i < oc) 245 System.arraycopy(o.path, o.offsets[i], 246 result, pos, 247 o.path.length - o.offsets[i]); 248 return new JrtPath(getFileSystem(), result); 249 } 250 251 @Override 252 public JrtFileSystem getFileSystem() { 253 return jrtfs; 254 } 255 256 @Override 257 public boolean isAbsolute() { 258 return (this.path.length > 0 && path[0] == '/'); 259 } 260 261 @Override 262 public JrtPath resolve(Path other) { 263 final JrtPath o = checkPath(other); 264 if (o.isAbsolute()) 265 return o; 266 byte[] res; 267 if (this.path[path.length - 1] == '/') { 268 res = new byte[path.length + o.path.length]; 269 System.arraycopy(path, 0, res, 0, path.length); 270 System.arraycopy(o.path, 0, res, path.length, o.path.length); 271 } else { 272 res = new byte[path.length + 1 + o.path.length]; 273 System.arraycopy(path, 0, res, 0, path.length); 274 res[path.length] = '/'; 275 System.arraycopy(o.path, 0, res, path.length + 1, o.path.length); 276 } 277 return new JrtPath(jrtfs, res); 278 } 279 280 @Override 281 public Path resolveSibling(Path other) { 282 if (other == null) 283 throw new NullPointerException(); 284 Path parent = getParent(); 285 return (parent == null) ? other : parent.resolve(other); 286 } 287 288 @Override 289 public boolean startsWith(Path other) { 290 final JrtPath o = checkPath(other); 291 if (o.isAbsolute() != this.isAbsolute() || 292 o.path.length > this.path.length) 293 return false; 294 int olast = o.path.length; 295 for (int i = 0; i < olast; i++) { 296 if (o.path[i] != this.path[i]) 297 return false; 298 } 299 olast--; 300 return o.path.length == this.path.length || 301 o.path[olast] == '/' || 302 this.path[olast + 1] == '/'; 303 } 304 305 @Override 306 public boolean endsWith(Path other) { 307 final JrtPath o = checkPath(other); 308 int olast = o.path.length - 1; 309 if (olast > 0 && o.path[olast] == '/') 310 olast--; 311 int last = this.path.length - 1; 312 if (last > 0 && this.path[last] == '/') 313 last--; 314 if (olast == -1) // o.path.length == 0 315 return last == -1; 316 if ((o.isAbsolute() &&(!this.isAbsolute() || olast != last)) || 317 (last < olast)) 318 return false; 319 for (; olast >= 0; olast--, last--) { 320 if (o.path[olast] != this.path[last]) 321 return false; 322 } 323 return o.path[olast + 1] == '/' || 324 last == -1 || this.path[last] == '/'; 325 } 326 327 @Override 328 public JrtPath resolve(String other) { 329 return resolve(getFileSystem().getPath(other)); 330 } 331 332 @Override 333 public final Path resolveSibling(String other) { 334 return resolveSibling(getFileSystem().getPath(other)); 335 } 336 337 @Override 338 public final boolean startsWith(String other) { 339 return startsWith(getFileSystem().getPath(other)); 340 } 341 342 @Override 343 public final boolean endsWith(String other) { 344 return endsWith(getFileSystem().getPath(other)); 345 } 346 347 @Override 348 public Path normalize() { 349 byte[] res = getResolved(); 350 if (res == path) // no change 351 return this; 352 return new JrtPath(jrtfs, res, true); 353 } 354 355 private JrtPath checkPath(Path path) { 356 if (path == null) 357 throw new NullPointerException(); 358 if (!(path instanceof JrtPath)) 359 throw new ProviderMismatchException(); 360 return (JrtPath) path; 361 } 362 363 // create offset list if not already created 364 private void initOffsets() { 365 if (offsets == null) { 366 int count, index; 367 // count names 368 count = 0; 369 index = 0; 370 while (index < path.length) { 371 byte c = path[index++]; 372 if (c != '/') { 373 count++; 374 while (index < path.length && path[index] != '/') 375 index++; 376 } 377 } 378 // populate offsets 379 int[] result = new int[count]; 380 count = 0; 381 index = 0; 382 while (index < path.length) { 383 byte c = path[index]; 384 if (c == '/') { 385 index++; 386 } else { 387 result[count++] = index++; 388 while (index < path.length && path[index] != '/') 389 index++; 390 } 391 } 392 synchronized (this) { 393 if (offsets == null) 394 offsets = result; 395 } 396 } 397 } 398 399 // resolved path for locating jrt entry inside the jrt file, 400 // the result path does not contain ./ and .. components 401 // resolved bytes will always start with '/' 402 private volatile byte[] resolved = null; 403 byte[] getResolvedPath() { 404 byte[] r = resolved; 405 if (r == null) { 406 if (isAbsolute()) 407 r = getResolved(); 408 else 409 r = toAbsolutePath().getResolvedPath(); 410 resolved = r; 411 } 412 return resolved; 413 } 414 415 // removes redundant slashs, replace "\" to separator "/" 416 // and check for invalid characters 417 private static byte[] normalize(byte[] path) { 418 if (path.length == 0) 419 return path; 420 byte prevC = 0; 421 for (int i = 0; i < path.length; i++) { 422 byte c = path[i]; 423 if (c == '\\') 424 return normalize(path, i); 425 if (c == (byte)'/' && prevC == '/') 426 return normalize(path, i - 1); 427 if (c == '\u0000') 428 throw new InvalidPathException(JrtFileSystem.getString(path), 429 "Path: nul character not allowed"); 430 prevC = c; 431 } 432 433 if (path.length > 1 && path[path.length - 1] == '/') { 434 return Arrays.copyOf(path, path.length - 1); 435 } 436 437 return path; 438 } 439 440 private static byte[] normalize(byte[] path, int off) { 441 byte[] to = new byte[path.length]; 442 int n = 0; 443 while (n < off) { 444 to[n] = path[n]; 445 n++; 446 } 447 int m = n; 448 byte prevC = 0; 449 while (n < path.length) { 450 byte c = path[n++]; 451 if (c == (byte)'\\') 452 c = (byte)'/'; 453 if (c == (byte)'/' && prevC == (byte)'/') 454 continue; 455 if (c == '\u0000') 456 throw new InvalidPathException(JrtFileSystem.getString(path), 457 "Path: nul character not allowed"); 458 to[m++] = c; 459 prevC = c; 460 } 461 if (m > 1 && to[m - 1] == '/') 462 m--; 463 return (m == to.length)? to : Arrays.copyOf(to, m); 464 } 465 466 // Remove DotSlash(./) and resolve DotDot (..) components 467 private byte[] getResolved() { 468 if (path.length == 0) 469 return path; 470 for (int i = 0; i < path.length; i++) { 471 byte c = path[i]; 472 if (c == (byte)'.') 473 return resolve0(); 474 } 475 476 return path; 477 } 478 479 // TBD: performance, avoid initOffsets 480 private byte[] resolve0() { 481 byte[] to = new byte[path.length]; 482 int nc = getNameCount(); 483 int[] lastM = new int[nc]; 484 int lastMOff = -1; 485 int m = 0; 486 for (int i = 0; i < nc; i++) { 487 int n = offsets[i]; 488 int len = (i == offsets.length - 1)? 489 (path.length - n):(offsets[i + 1] - n - 1); 490 if (len == 1 && path[n] == (byte)'.') { 491 if (m == 0 && path[0] == '/') // absolute path 492 to[m++] = '/'; 493 continue; 494 } 495 if (len == 2 && path[n] == '.' && path[n + 1] == '.') { 496 if (lastMOff >= 0) { 497 m = lastM[lastMOff--]; // retreat 498 continue; 499 } 500 if (path[0] == '/') { // "/../xyz" skip 501 if (m == 0) 502 to[m++] = '/'; 503 } else { // "../xyz" -> "../xyz" 504 if (m != 0 && to[m-1] != '/') 505 to[m++] = '/'; 506 while (len-- > 0) 507 to[m++] = path[n++]; 508 } 509 continue; 510 } 511 if (m == 0 && path[0] == '/' || // absolute path 512 m != 0 && to[m-1] != '/') { // not the first name 513 to[m++] = '/'; 514 } 515 lastM[++lastMOff] = m; 516 while (len-- > 0) 517 to[m++] = path[n++]; 518 } 519 if (m > 1 && to[m - 1] == '/') 520 m--; 521 return (m == to.length)? to : Arrays.copyOf(to, m); 522 } 523 524 @Override 525 public String toString() { 526 return JrtFileSystem.getString(path); 527 } 528 529 @Override 530 public int hashCode() { 531 int h = hashcode; 532 if (h == 0) 533 hashcode = h = Arrays.hashCode(path); 534 return h; 535 } 536 537 @Override 538 public boolean equals(Object obj) { 539 return obj != null && 540 obj instanceof JrtPath && 541 this.jrtfs == ((JrtPath)obj).jrtfs && 542 compareTo((Path) obj) == 0; 543 } 544 545 @Override 546 public int compareTo(Path other) { 547 final JrtPath o = checkPath(other); 548 int len1 = this.path.length; 549 int len2 = o.path.length; 550 551 int n = Math.min(len1, len2); 552 byte v1[] = this.path; 553 byte v2[] = o.path; 554 555 int k = 0; 556 while (k < n) { 557 int c1 = v1[k] & 0xff; 558 int c2 = v2[k] & 0xff; 559 if (c1 != c2) 560 return c1 - c2; 561 k++; 562 } 563 return len1 - len2; 564 } 565 566 @Override 567 public WatchKey register( 568 WatchService watcher, 569 WatchEvent.Kind<?>[] events, 570 WatchEvent.Modifier... modifiers) { 571 if (watcher == null || events == null || modifiers == null) { 572 throw new NullPointerException(); 573 } 574 throw new UnsupportedOperationException(); 575 } 576 577 @Override 578 public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) { 579 return register(watcher, events, new WatchEvent.Modifier[0]); 580 } 581 582 @Override 583 public final File toFile() { 584 throw new UnsupportedOperationException(); 585 } 586 587 @Override 588 public Iterator<Path> iterator() { 589 return new Iterator<Path>() { 590 private int i = 0; 591 592 @Override 593 public boolean hasNext() { 594 return (i < getNameCount()); 595 } 596 597 @Override 598 public Path next() { 599 if (i < getNameCount()) { 600 Path result = getName(i); 601 i++; 602 return result; 603 } else { 604 throw new NoSuchElementException(); 605 } 606 } 607 608 @Override 609 public void remove() { 610 throw new ReadOnlyFileSystemException(); 611 } 612 }; 613 } 614 615 ///////////////////////////////////////////////////////////////////// 616 // Helpers for JrtFileSystemProvider and JrtFileSystem 617 618 int getPathLength() { 619 return path.length; 620 } 621 622 623 void createDirectory(FileAttribute<?>... attrs) 624 throws IOException 625 { 626 jrtfs.createDirectory(getResolvedPath(), attrs); 627 } 628 629 InputStream newInputStream(OpenOption... options) throws IOException 630 { 631 if (options.length > 0) { 632 for (OpenOption opt : options) { 633 if (opt != READ) 634 throw new UnsupportedOperationException("'" + opt + "' not allowed"); 635 } 636 } 637 return jrtfs.newInputStream(getResolvedPath()); 638 } 639 640 DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter) 641 throws IOException 642 { 643 return new JrtDirectoryStream(this, filter); 644 } 645 646 void delete() throws IOException { 647 jrtfs.deleteFile(getResolvedPath(), true); 648 } 649 650 void deleteIfExists() throws IOException { 651 jrtfs.deleteFile(getResolvedPath(), false); 652 } 653 654 JrtFileAttributes getAttributes(LinkOption... options) throws IOException 655 { 656 JrtFileAttributes zfas = jrtfs.getFileAttributes(getResolvedPath(), options); 657 if (zfas == null) 658 throw new NoSuchFileException(toString()); 659 return zfas; 660 } 661 662 void setAttribute(String attribute, Object value, LinkOption... options) 663 throws IOException 664 { 665 String type; 666 String attr; 667 int colonPos = attribute.indexOf(':'); 668 if (colonPos == -1) { 669 type = "basic"; 670 attr = attribute; 671 } else { 672 type = attribute.substring(0, colonPos++); 673 attr = attribute.substring(colonPos); 674 } 675 JrtFileAttributeView view = JrtFileAttributeView.get(this, type, options); 676 if (view == null) 677 throw new UnsupportedOperationException("view <" + view + "> is not supported"); 678 view.setAttribute(attr, value); 679 } 680 681 void setTimes(FileTime mtime, FileTime atime, FileTime ctime) 682 throws IOException 683 { 684 jrtfs.setTimes(getResolvedPath(), mtime, atime, ctime); 685 } 686 687 Map<String, Object> readAttributes(String attributes, LinkOption... options) 688 throws IOException 689 690 { 691 String view; 692 String attrs; 693 int colonPos = attributes.indexOf(':'); 694 if (colonPos == -1) { 695 view = "basic"; 696 attrs = attributes; 697 } else { 698 view = attributes.substring(0, colonPos++); 699 attrs = attributes.substring(colonPos); 700 } 701 JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view, options); 702 if (jrtfv == null) { 703 throw new UnsupportedOperationException("view not supported"); 704 } 705 return jrtfv.readAttributes(attrs); 706 } 707 708 FileStore getFileStore() throws IOException { 709 // each JrtFileSystem only has one root (as requested for now) 710 if (exists()) 711 return jrtfs.getFileStore(this); 712 throw new NoSuchFileException(JrtFileSystem.getString(path)); 713 } 714 715 boolean isSameFile(Path other) throws IOException { 716 if (this.equals(other)) 717 return true; 718 if (other == null || 719 this.getFileSystem() != other.getFileSystem()) 720 return false; 721 this.checkAccess(); 722 JrtPath path = (JrtPath)other; 723 path.checkAccess(); 724 return Arrays.equals(this.getResolvedPath(), path.getResolvedPath()) || 725 jrtfs.isSameFile(this, (JrtPath)other); 726 } 727 728 SeekableByteChannel newByteChannel(Set<? extends OpenOption> options, 729 FileAttribute<?>... attrs) 730 throws IOException 731 { 732 return jrtfs.newByteChannel(getResolvedPath(), options, attrs); 733 } 734 735 736 FileChannel newFileChannel(Set<? extends OpenOption> options, 737 FileAttribute<?>... attrs) 738 throws IOException 739 { 740 return jrtfs.newFileChannel(getResolvedPath(), options, attrs); 741 } 742 743 void checkAccess(AccessMode... modes) throws IOException { 744 boolean w = false; 745 boolean x = false; 746 for (AccessMode mode : modes) { 747 switch (mode) { 748 case READ: 749 break; 750 case WRITE: 751 w = true; 752 break; 753 case EXECUTE: 754 x = true; 755 break; 756 default: 757 throw new UnsupportedOperationException(); 758 } 759 } 760 JrtFileAttributes attrs = jrtfs.getFileAttributes(getResolvedPath()); 761 if (attrs == null && (path.length != 1 || path[0] != '/')) 762 throw new NoSuchFileException(toString()); 763 if (w) { 764 if (jrtfs.isReadOnly()) 765 throw new AccessDeniedException(toString()); 766 } 767 if (x) 768 throw new AccessDeniedException(toString()); 769 } 770 771 boolean exists() { 772 if (isAbsolute()) 773 return true; 774 try { 775 return jrtfs.exists(getResolvedPath()); 776 } catch (IOException x) {} 777 return false; 778 } 779 780 OutputStream newOutputStream(OpenOption... options) throws IOException 781 { 782 if (options.length == 0) 783 return jrtfs.newOutputStream(getResolvedPath(), 784 CREATE_NEW, WRITE); 785 return jrtfs.newOutputStream(getResolvedPath(), options); 786 } 787 788 void move(JrtPath target, CopyOption... options) 789 throws IOException 790 { 791 if (this.jrtfs == target.jrtfs) 792 { 793 jrtfs.copyFile(true, 794 getResolvedPath(), target.getResolvedPath(), 795 options); 796 } else { 797 copyToTarget(target, options); 798 delete(); 799 } 800 } 801 802 void copy(JrtPath target, CopyOption... options) 803 throws IOException 804 { 805 if (this.jrtfs == target.jrtfs) 806 jrtfs.copyFile(false, 807 getResolvedPath(), target.getResolvedPath(), 808 options); 809 else 810 copyToTarget(target, options); 811 } 812 813 private void copyToTarget(JrtPath target, CopyOption... options) 814 throws IOException 815 { 816 boolean replaceExisting = false; 817 boolean copyAttrs = false; 818 for (CopyOption opt : options) { 819 if (opt == REPLACE_EXISTING) 820 replaceExisting = true; 821 else if (opt == COPY_ATTRIBUTES) 822 copyAttrs = true; 823 } 824 // attributes of source file 825 JrtFileAttributes jrtfas = getAttributes(); 826 // check if target exists 827 boolean exists; 828 if (replaceExisting) { 829 try { 830 target.deleteIfExists(); 831 exists = false; 832 } catch (DirectoryNotEmptyException x) { 833 exists = true; 834 } 835 } else { 836 exists = target.exists(); 837 } 838 if (exists) 839 throw new FileAlreadyExistsException(target.toString()); 840 841 if (jrtfas.isDirectory()) { 842 // create directory or file 843 target.createDirectory(); 844 } else { 845 try (InputStream is = jrtfs.newInputStream(getResolvedPath()); OutputStream os = target.newOutputStream()) { 846 byte[] buf = new byte[8192]; 847 int n; 848 while ((n = is.read(buf)) != -1) { 849 os.write(buf, 0, n); 850 } 851 } 852 } 853 if (copyAttrs) { 854 BasicFileAttributeView view = 855 JrtFileAttributeView.get(target, BasicFileAttributeView.class); 856 try { 857 view.setTimes(jrtfas.lastModifiedTime(), 858 jrtfas.lastAccessTime(), 859 jrtfas.creationTime()); 860 } catch (IOException x) { 861 // rollback? 862 try { 863 target.delete(); 864 } catch (IOException ignore) { } 865 throw x; 866 } 867 } 868 } 869 }