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