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