1 /* 2 * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.nio.*; 29 import java.nio.file.*; 30 import java.nio.file.attribute.*; 31 import java.nio.charset.*; 32 import java.nio.channels.*; 33 import java.security.AccessController; 34 import java.io.*; 35 import java.net.URI; 36 import java.util.*; 37 import java.lang.ref.SoftReference; 38 import sun.security.util.SecurityConstants; 39 40 import static sun.nio.fs.UnixNativeDispatcher.*; 41 import static sun.nio.fs.UnixConstants.*; 42 43 /** 44 * Solaris/Linux implementation of java.nio.file.Path 45 */ 46 47 class UnixPath 48 extends AbstractPath 49 { 50 private static ThreadLocal<SoftReference<CharsetEncoder>> encoder = 51 new ThreadLocal<SoftReference<CharsetEncoder>>(); 52 53 // FIXME - eliminate this reference to reduce space 54 private final UnixFileSystem fs; 55 56 // internal representation 57 private final byte[] path; 58 59 // String representation (created lazily) 60 private volatile String stringValue; 61 62 // cached hashcode (created lazily, no need to be volatile) 63 private int hash; 64 65 // array of offsets of elements in path (created lazily) 66 private volatile int[] offsets; 67 68 UnixPath(UnixFileSystem fs, byte[] path) { 69 this.fs = fs; 70 this.path = path; 71 } 72 73 UnixPath(UnixFileSystem fs, String input) { 74 // removes redundant slashes and checks for invalid characters 75 this(fs, encode(normalizeAndCheck(input))); 76 } 77 78 // package-private 79 // removes redundant slashes and check input for invalid characters 80 static String normalizeAndCheck(String input) { 81 int n = input.length(); 82 if (n == 0) 83 throw new InvalidPathException(input, "Path is empty"); 84 char prevChar = 0; 85 for (int i=0; i < n; i++) { 86 char c = input.charAt(i); 87 if ((c == '/') && (prevChar == '/')) 88 return normalize(input, n, i - 1); 89 checkNotNul(input, c); 90 prevChar = c; 91 } 92 if (prevChar == '/') 93 return normalize(input, n, n - 1); 94 return input; 95 } 96 97 private static void checkNotNul(String input, char c) { 98 if (c == '\u0000') 99 throw new InvalidPathException(input, "Nul character not allowed"); 100 } 101 102 private static String normalize(String input, int len, int off) { 103 if (len == 0) 104 return input; 105 int n = len; 106 while ((n > 0) && (input.charAt(n - 1) == '/')) n--; 107 if (n == 0) 108 return "/"; 109 StringBuilder sb = new StringBuilder(input.length()); 110 if (off > 0) 111 sb.append(input.substring(0, off)); 112 char prevChar = 0; 113 for (int i=off; i < n; i++) { 114 char c = input.charAt(i); 115 if ((c == '/') && (prevChar == '/')) 116 continue; 117 checkNotNul(input, c); 118 sb.append(c); 119 prevChar = c; 120 } 121 return sb.toString(); 122 } 123 124 // encodes the given path-string into a sequence of bytes 125 private static byte[] encode(String input) { 126 SoftReference<CharsetEncoder> ref = encoder.get(); 127 CharsetEncoder ce = (ref != null) ? ref.get() : null; 128 if (ce == null) { 129 ce = Charset.defaultCharset().newEncoder() 130 .onMalformedInput(CodingErrorAction.REPORT) 131 .onUnmappableCharacter(CodingErrorAction.REPORT); 132 encoder.set(new SoftReference<CharsetEncoder>(ce)); 133 } 134 135 char[] ca = input.toCharArray(); 136 137 // size output buffer for worse-case size 138 byte[] ba = new byte[(int)(ca.length * (double)ce.maxBytesPerChar())]; 139 140 // encode 141 ByteBuffer bb = ByteBuffer.wrap(ba); 142 CharBuffer cb = CharBuffer.wrap(ca); 143 ce.reset(); 144 CoderResult cr = ce.encode(cb, bb, true); 145 boolean error; 146 if (!cr.isUnderflow()) { 147 error = true; 148 } else { 149 cr = ce.flush(bb); 150 error = !cr.isUnderflow(); 151 } 152 if (error) { 153 throw new InvalidPathException(input, 154 "Malformed input or input contains unmappable chacraters"); 155 } 156 157 // trim result to actual length if required 158 int len = bb.position(); 159 if (len != ba.length) 160 ba = Arrays.copyOf(ba, len); 161 162 return ba; 163 } 164 165 // package-private 166 byte[] asByteArray() { 167 return path; 168 } 169 170 // use this path when making system/library calls 171 byte[] getByteArrayForSysCalls() { 172 // resolve against default directory if required (chdir allowed or 173 // file system default directory is not working directory) 174 if (getFileSystem().needToResolveAgainstDefaultDirectory()) { 175 return resolve(getFileSystem().defaultDirectory(), path); 176 } else { 177 return path; 178 } 179 } 180 181 // use this message when throwing exceptions 182 String getPathForExecptionMessage() { 183 return toString(); 184 } 185 186 // use this path for permission checks 187 String getPathForPermissionCheck() { 188 if (getFileSystem().needToResolveAgainstDefaultDirectory()) { 189 return new String(getByteArrayForSysCalls()); 190 } else { 191 return toString(); 192 } 193 } 194 195 // Checks that the given file is a UnixPath 196 private UnixPath checkPath(FileRef obj) { 197 if (obj == null) 198 throw new NullPointerException(); 199 if (!(obj instanceof UnixPath)) 200 throw new ProviderMismatchException(); 201 return (UnixPath)obj; 202 } 203 204 // create offset list if not already created 205 private void initOffsets() { 206 if (offsets == null) { 207 int count, index; 208 209 // count names 210 count = 0; 211 index = 0; 212 while (index < path.length) { 213 byte c = path[index++]; 214 if (c != '/') { 215 count++; 216 while (index < path.length && path[index] != '/') 217 index++; 218 } 219 } 220 221 // populate offsets 222 int[] result = new int[count]; 223 count = 0; 224 index = 0; 225 while (index < path.length) { 226 byte c = path[index]; 227 if (c == '/') { 228 index++; 229 } else { 230 result[count++] = index++; 231 while (index < path.length && path[index] != '/') 232 index++; 233 } 234 } 235 synchronized (this) { 236 if (offsets == null) 237 offsets = result; 238 } 239 } 240 } 241 242 @Override 243 public UnixFileSystem getFileSystem() { 244 return fs; 245 } 246 247 @Override 248 public UnixPath getRoot() { 249 if (path[0] == '/') { 250 return getFileSystem().rootDirectory(); 251 } else { 252 return null; 253 } 254 } 255 256 @Override 257 public UnixPath getName() { 258 initOffsets(); 259 260 int count = offsets.length; 261 if (count == 0) 262 return null; // no elements so no name 263 264 if (count == 1 && path[0] != '/') 265 return this; 266 267 int lastOffset = offsets[count-1]; 268 int len = path.length - lastOffset; 269 byte[] result = new byte[len]; 270 System.arraycopy(path, lastOffset, result, 0, len); 271 return new UnixPath(getFileSystem(), result); 272 } 273 274 @Override 275 public UnixPath getParent() { 276 initOffsets(); 277 278 int count = offsets.length; 279 if (count == 0) { 280 // no elements so no parent 281 return null; 282 } 283 int len = offsets[count-1] - 1; 284 if (len <= 0) { 285 // parent is root only (may be null) 286 return getRoot(); 287 } 288 byte[] result = new byte[len]; 289 System.arraycopy(path, 0, result, 0, len); 290 return new UnixPath(getFileSystem(), result); 291 } 292 293 @Override 294 public int getNameCount() { 295 initOffsets(); 296 return offsets.length; 297 } 298 299 @Override 300 public UnixPath getName(int index) { 301 initOffsets(); 302 if (index < 0) 303 throw new IllegalArgumentException(); 304 if (index >= offsets.length) 305 throw new IllegalArgumentException(); 306 307 int begin = offsets[index]; 308 int len; 309 if (index == (offsets.length-1)) { 310 len = path.length - begin; 311 } else { 312 len = offsets[index+1] - begin - 1; 313 } 314 315 // construct result 316 byte[] result = new byte[len]; 317 System.arraycopy(path, begin, result, 0, len); 318 return new UnixPath(getFileSystem(), result); 319 } 320 321 @Override 322 public UnixPath subpath(int beginIndex, int endIndex) { 323 initOffsets(); 324 325 if (beginIndex < 0) 326 throw new IllegalArgumentException(); 327 if (beginIndex >= offsets.length) 328 throw new IllegalArgumentException(); 329 if (endIndex > offsets.length) 330 throw new IllegalArgumentException(); 331 if (beginIndex >= endIndex) { 332 throw new IllegalArgumentException(); 333 } 334 335 // starting offset and length 336 int begin = offsets[beginIndex]; 337 int len; 338 if (endIndex == offsets.length) { 339 len = path.length - begin; 340 } else { 341 len = offsets[endIndex] - begin - 1; 342 } 343 344 // construct result 345 byte[] result = new byte[len]; 346 System.arraycopy(path, begin, result, 0, len); 347 return new UnixPath(getFileSystem(), result); 348 } 349 350 @Override 351 public boolean isAbsolute() { 352 return (path[0] == '/'); 353 } 354 355 // Resolve child against given base 356 private static byte[] resolve(byte[] base, byte[] child) { 357 if (child[0] == '/') 358 return child; 359 byte[] result; 360 if (base.length == 1 && base[0] == '/') { 361 result = new byte[child.length + 1]; 362 result[0] = '/'; 363 System.arraycopy(child, 0, result, 1, child.length); 364 } else { 365 result = new byte[base.length + 1 + child.length]; 366 System.arraycopy(base, 0, result, 0, base.length); 367 result[base.length] = '/'; 368 System.arraycopy(child, 0, result, base.length+1, child.length); 369 } 370 return result; 371 } 372 373 @Override 374 public UnixPath resolve(Path obj) { 375 if (obj == null) 376 return this; 377 byte[] other = checkPath(obj).path; 378 if (other[0] == '/') 379 return ((UnixPath)obj); 380 byte[] result = resolve(path, other); 381 return new UnixPath(getFileSystem(), result); 382 } 383 384 @Override 385 public UnixPath resolve(String other) { 386 return resolve(new UnixPath(getFileSystem(), other)); 387 } 388 389 UnixPath resolve(byte[] other) { 390 return resolve(new UnixPath(getFileSystem(), other)); 391 } 392 393 @Override 394 public UnixPath relativize(Path obj) { 395 UnixPath other = checkPath(obj); 396 if (other.equals(this)) 397 return null; 398 399 // can only relativize paths of the same type 400 if (this.isAbsolute() != other.isAbsolute()) 401 throw new IllegalArgumentException("'other' is different type of Path"); 402 403 int bn = this.getNameCount(); 404 int cn = other.getNameCount(); 405 406 // skip matching names 407 int n = (bn > cn) ? cn : bn; 408 int i = 0; 409 while (i < n) { 410 if (!this.getName(i).equals(other.getName(i))) 411 break; 412 i++; 413 } 414 415 int dotdots = bn - i; 416 if (i < cn) { 417 // remaining name components in other 418 UnixPath remainder = other.subpath(i, cn); 419 if (dotdots == 0) 420 return remainder; 421 422 // result is a "../" for each remaining name in base 423 // followed by the remaining names in other 424 byte[] result = new byte[dotdots*3 + remainder.path.length]; 425 int pos = 0; 426 while (dotdots > 0) { 427 result[pos++] = (byte)'.'; 428 result[pos++] = (byte)'.'; 429 result[pos++] = (byte)'/'; 430 dotdots--; 431 } 432 System.arraycopy(remainder.path, 0, result, pos, remainder.path.length); 433 return new UnixPath(getFileSystem(), result); 434 } else { 435 // no remaining names in other so result is simply a sequence of ".." 436 byte[] result = new byte[dotdots*3 - 1]; 437 int pos = 0; 438 while (dotdots > 0) { 439 result[pos++] = (byte)'.'; 440 result[pos++] = (byte)'.'; 441 // no tailing slash at the end 442 if (dotdots > 1) 443 result[pos++] = (byte)'/'; 444 dotdots--; 445 } 446 return new UnixPath(getFileSystem(), result); 447 } 448 } 449 450 @Override 451 public Path normalize() { 452 final int count = getNameCount(); 453 if (count == 0) 454 return this; 455 456 boolean[] ignore = new boolean[count]; // true => ignore name 457 int[] size = new int[count]; // length of name 458 int remaining = count; // number of names remaining 459 boolean hasDotDot = false; // has at least one .. 460 boolean isAbsolute = path[0] == '/'; 461 462 // first pass: 463 // 1. compute length of names 464 // 2. mark all occurences of "." to ignore 465 // 3. and look for any occurences of ".." 466 for (int i=0; i<count; i++) { 467 int begin = offsets[i]; 468 int len; 469 if (i == (offsets.length-1)) { 470 len = path.length - begin; 471 } else { 472 len = offsets[i+1] - begin - 1; 473 } 474 size[i] = len; 475 476 if (path[begin] == '.') { 477 if (len == 1) { 478 ignore[i] = true; // ignore "." 479 remaining--; 480 } 481 else { 482 if (path[begin+1] == '.') // ".." found 483 hasDotDot = true; 484 } 485 } 486 } 487 488 // multiple passes to eliminate all occurences of name/.. 489 if (hasDotDot) { 490 int prevRemaining; 491 do { 492 prevRemaining = remaining; 493 int prevName = -1; 494 for (int i=0; i<count; i++) { 495 if (ignore[i]) 496 continue; 497 498 // not a ".." 499 if (size[i] != 2) { 500 prevName = i; 501 continue; 502 } 503 504 int begin = offsets[i]; 505 if (path[begin] != '.' || path[begin+1] != '.') { 506 prevName = i; 507 continue; 508 } 509 510 // ".." found 511 if (prevName >= 0) { 512 // name/<ignored>/.. found so mark name and ".." to be 513 // ignored 514 ignore[prevName] = true; 515 ignore[i] = true; 516 remaining = remaining - 2; 517 prevName = -1; 518 } else { 519 // Case: /<ignored>/.. so mark ".." as ignored 520 if (isAbsolute) { 521 boolean hasPrevious = false; 522 for (int j=0; j<i; j++) { 523 if (!ignore[j]) { 524 hasPrevious = true; 525 break; 526 } 527 } 528 if (!hasPrevious) { 529 // all proceeding names are ignored 530 ignore[i] = true; 531 remaining--; 532 } 533 } 534 } 535 } 536 } while (prevRemaining > remaining); 537 } 538 539 // no redundant names 540 if (remaining == count) 541 return this; 542 543 // corner case - all names removed 544 if (remaining == 0) { 545 return isAbsolute ? getFileSystem().rootDirectory() : null; 546 } 547 548 // compute length of result 549 int len = remaining - 1; 550 if (isAbsolute) 551 len++; 552 553 for (int i=0; i<count; i++) { 554 if (!ignore[i]) 555 len += size[i]; 556 } 557 byte[] result = new byte[len]; 558 559 // copy names into result 560 int pos = 0; 561 if (isAbsolute) 562 result[pos++] = '/'; 563 for (int i=0; i<count; i++) { 564 if (!ignore[i]) { 565 System.arraycopy(path, offsets[i], result, pos, size[i]); 566 pos += size[i]; 567 if (--remaining > 0) { 568 result[pos++] = '/'; 569 } 570 } 571 } 572 return new UnixPath(getFileSystem(), result); 573 } 574 575 @Override 576 public boolean startsWith(Path other) { 577 UnixPath that = checkPath(other); 578 579 // other path is longer 580 if (that.path.length > path.length) 581 return false; 582 583 int thisOffsetCount = getNameCount(); 584 int thatOffsetCount = that.getNameCount(); 585 586 // other path has no name elements 587 if (thatOffsetCount == 0 && this.isAbsolute()) 588 return true; 589 590 // given path has more elements that this path 591 if (thatOffsetCount > thisOffsetCount) 592 return false; 593 594 // same number of elements so must be exact match 595 if ((thatOffsetCount == thisOffsetCount) && 596 (path.length != that.path.length)) { 597 return false; 598 } 599 600 // check offsets of elements match 601 for (int i=0; i<thatOffsetCount; i++) { 602 Integer o1 = offsets[i]; 603 Integer o2 = that.offsets[i]; 604 if (!o1.equals(o2)) 605 return false; 606 } 607 608 // offsets match so need to compare bytes 609 int i=0; 610 while (i < that.path.length) { 611 if (this.path[i] != that.path[i]) 612 return false; 613 i++; 614 } 615 616 // final check that match is on name boundary 617 if (i < path.length && this.path[i] != '/') 618 return false; 619 620 return true; 621 } 622 623 @Override 624 public boolean endsWith(Path other) { 625 UnixPath that = checkPath(other); 626 627 // other path is longer 628 if (that.path.length > path.length) 629 return false; 630 631 // other path is absolute so this path must be absolute 632 if (that.isAbsolute() && !this.isAbsolute()) 633 return false; 634 635 int thisOffsetCount = getNameCount(); 636 int thatOffsetCount = that.getNameCount(); 637 638 // given path has more elements that this path 639 if (thatOffsetCount > thisOffsetCount) { 640 return false; 641 } else { 642 // same number of elements 643 if (thatOffsetCount == thisOffsetCount) { 644 if (thisOffsetCount == 0) 645 return true; 646 int expectedLen = path.length; 647 if (this.isAbsolute() && !that.isAbsolute()) 648 expectedLen--; 649 if (that.path.length != expectedLen) 650 return false; 651 } else { 652 // this path has more elements so given path must be relative 653 if (that.isAbsolute()) 654 return false; 655 } 656 } 657 658 // compare bytes 659 int thisPos = offsets[thisOffsetCount - thatOffsetCount]; 660 int thatPos = that.offsets[0]; 661 while (thatPos < that.path.length) { 662 if (this.path[thisPos++] != that.path[thatPos++]) 663 return false; 664 } 665 666 return true; 667 } 668 669 @Override 670 public int compareTo(Path other) { 671 int len1 = path.length; 672 int len2 = ((UnixPath) other).path.length; 673 674 int n = Math.min(len1, len2); 675 byte v1[] = path; 676 byte v2[] = ((UnixPath) other).path; 677 678 int k = 0; 679 while (k < n) { 680 int c1 = v1[k] & 0xff; 681 int c2 = v2[k] & 0xff; 682 if (c1 != c2) { 683 return c1 - c2; 684 } 685 k++; 686 } 687 return len1 - len2; 688 } 689 690 @Override 691 public boolean equals(Object ob) { 692 if ((ob != null) && (ob instanceof UnixPath)) { 693 return compareTo((Path)ob) == 0; 694 } 695 return false; 696 } 697 698 @Override 699 public int hashCode() { 700 // OK if two or more threads compute hash 701 int h = hash; 702 if (h == 0) { 703 for (int i = 0; i< path.length; i++) { 704 h = 31*h + (path[i] & 0xff); 705 } 706 hash = h; 707 } 708 return h; 709 } 710 711 @Override 712 public String toString() { 713 // OK if two or more threads create a String 714 if (stringValue == null) 715 stringValue = new String(path); // platform encoding 716 return stringValue; 717 } 718 719 @Override 720 public Iterator<Path> iterator() { 721 initOffsets(); 722 return new Iterator<Path>() { 723 int i = 0; 724 @Override 725 public boolean hasNext() { 726 return (i < offsets.length); 727 } 728 @Override 729 public Path next() { 730 if (i < offsets.length) { 731 Path result = getName(i); 732 i++; 733 return result; 734 } else { 735 throw new NoSuchElementException(); 736 } 737 } 738 @Override 739 public void remove() { 740 throw new UnsupportedOperationException(); 741 } 742 }; 743 } 744 745 // -- file operations -- 746 747 // package-private 748 int openForAttributeAccess(boolean followLinks) throws IOException { 749 int flags = O_RDONLY; 750 if (!followLinks) 751 flags |= O_NOFOLLOW; 752 try { 753 return open(this, flags, 0); 754 } catch (UnixException x) { 755 // HACK: EINVAL instead of ELOOP on Solaris 10 prior to u4 (see 6460380) 756 if (getFileSystem().isSolaris() && x.errno() == EINVAL) 757 x.setError(ELOOP); 758 759 if (x.errno() == ELOOP) 760 throw new FileSystemException(getPathForExecptionMessage(), null, 761 x.getMessage() + " or unable to access attributes of symbolic link"); 762 763 x.rethrowAsIOException(this); 764 return -1; // keep compile happy 765 } 766 } 767 768 769 void checkRead() { 770 SecurityManager sm = System.getSecurityManager(); 771 if (sm != null) 772 sm.checkRead(getPathForPermissionCheck()); 773 } 774 775 void checkWrite() { 776 SecurityManager sm = System.getSecurityManager(); 777 if (sm != null) 778 sm.checkWrite(getPathForPermissionCheck()); 779 } 780 781 void checkDelete() { 782 SecurityManager sm = System.getSecurityManager(); 783 if (sm != null) 784 sm.checkDelete(getPathForPermissionCheck()); 785 } 786 787 @Override 788 public FileStore getFileStore() 789 throws IOException 790 { 791 SecurityManager sm = System.getSecurityManager(); 792 if (sm != null) { 793 sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); 794 checkRead(); 795 } 796 return getFileSystem().getFileStore(this); 797 } 798 799 @Override 800 public void checkAccess(AccessMode... modes) throws IOException { 801 boolean e = false; 802 boolean r = false; 803 boolean w = false; 804 boolean x = false; 805 806 if (modes.length == 0) { 807 e = true; 808 } else { 809 for (AccessMode mode: modes) { 810 switch (mode) { 811 case READ : r = true; break; 812 case WRITE : w = true; break; 813 case EXECUTE : x = true; break; 814 default: throw new AssertionError("Should not get here"); 815 } 816 } 817 } 818 819 int mode = 0; 820 if (e || r) { 821 checkRead(); 822 mode |= (r) ? R_OK : F_OK; 823 } 824 if (w) { 825 checkWrite(); 826 mode |= W_OK; 827 } 828 if (x) { 829 SecurityManager sm = System.getSecurityManager(); 830 if (sm != null) { 831 // not cached 832 sm.checkExec(getPathForPermissionCheck()); 833 } 834 mode |= X_OK; 835 } 836 try { 837 access(this, mode); 838 } catch (UnixException exc) { 839 exc.rethrowAsIOException(this); 840 } 841 } 842 843 @Override 844 void implDelete(boolean failIfNotExists) throws IOException { 845 checkDelete(); 846 847 // need file attributes to know if file is directory 848 UnixFileAttributes attrs = null; 849 try { 850 attrs = UnixFileAttributes.get(this, false); 851 if (attrs.isDirectory()) { 852 rmdir(this); 853 } else { 854 unlink(this); 855 } 856 } catch (UnixException x) { 857 // no-op if file does not exist 858 if (!failIfNotExists && x.errno() == ENOENT) 859 return; 860 861 // DirectoryNotEmptyException if not empty 862 if (attrs != null && attrs.isDirectory() && 863 (x.errno() == EEXIST || x.errno() == ENOTEMPTY)) 864 throw new DirectoryNotEmptyException(getPathForExecptionMessage()); 865 866 x.rethrowAsIOException(this); 867 } 868 } 869 870 @Override 871 public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter) 872 throws IOException 873 { 874 if (filter == null) 875 throw new NullPointerException(); 876 checkRead(); 877 878 // can't return SecureDirectoryStream on kernels that don't support 879 // openat, etc. 880 if (!supportsAtSysCalls()) { 881 try { 882 long ptr = opendir(this); 883 return new UnixDirectoryStream(this, ptr, filter); 884 } catch (UnixException x) { 885 if (x.errno() == ENOTDIR) 886 throw new NotDirectoryException(getPathForExecptionMessage()); 887 x.rethrowAsIOException(this); 888 } 889 } 890 891 // open directory and dup file descriptor for use by 892 // opendir/readdir/closedir 893 int dfd1 = -1; 894 int dfd2 = -1; 895 long dp = 0L; 896 try { 897 dfd1 = open(this, O_RDONLY, 0); 898 dfd2 = dup(dfd1); 899 dp = fdopendir(dfd1); 900 } catch (UnixException x) { 901 if (dfd1 != -1) 902 close(dfd1); 903 if (dfd2 != -1) 904 close(dfd2); 905 if (x.errno() == UnixConstants.ENOTDIR) 906 throw new NotDirectoryException(getPathForExecptionMessage()); 907 x.rethrowAsIOException(this); 908 } 909 return new UnixSecureDirectoryStream(this, dp, dfd2, filter); 910 } 911 912 // invoked by AbstractPath#copyTo 913 @Override 914 public void implCopyTo(Path obj, CopyOption... options) 915 throws IOException 916 { 917 UnixPath target = (UnixPath)obj; 918 UnixCopyFile.copy(this, target, options); 919 } 920 921 @Override 922 public void implMoveTo(Path obj, CopyOption... options) 923 throws IOException 924 { 925 UnixPath target = (UnixPath)obj; 926 UnixCopyFile.move(this, target, options); 927 } 928 929 @Override 930 @SuppressWarnings("unchecked") 931 public <V extends FileAttributeView> V 932 getFileAttributeView(Class<V> type, LinkOption... options) 933 { 934 FileAttributeView view = getFileSystem() 935 .newFileAttributeView(type, this, options); 936 if (view == null) 937 return null; 938 return (V) view; 939 } 940 941 @Override 942 public DynamicFileAttributeView getFileAttributeView(String name, 943 LinkOption... options) 944 { 945 return getFileSystem().newFileAttributeView(name, this, options); 946 } 947 948 @Override 949 public Path createDirectory(FileAttribute<?>... attrs) 950 throws IOException 951 { 952 checkWrite(); 953 954 int mode = UnixFileModeAttribute 955 .toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs); 956 try { 957 mkdir(this, mode); 958 } catch (UnixException x) { 959 x.rethrowAsIOException(this); 960 } 961 return this; 962 } 963 964 @Override 965 public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options, 966 FileAttribute<?>... attrs) 967 throws IOException 968 { 969 int mode = UnixFileModeAttribute 970 .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs); 971 try { 972 return UnixChannelFactory.newFileChannel(this, options, mode); 973 } catch (UnixException x) { 974 x.rethrowAsIOException(this); 975 return null; // keep compiler happy 976 } 977 } 978 979 @Override 980 public boolean isSameFile(Path obj) throws IOException { 981 if (this.equals(obj)) 982 return true; 983 if (!(obj instanceof UnixPath)) // includes null check 984 return false; 985 UnixPath other = (UnixPath)obj; 986 987 // check security manager access to both files 988 this.checkRead(); 989 other.checkRead(); 990 991 UnixFileAttributes thisAttrs; 992 UnixFileAttributes otherAttrs; 993 try { 994 thisAttrs = UnixFileAttributes.get(this, true); 995 } catch (UnixException x) { 996 x.rethrowAsIOException(this); 997 return false; // keep compiler happy 998 } 999 try { 1000 otherAttrs = UnixFileAttributes.get(other, true); 1001 } catch (UnixException x) { 1002 x.rethrowAsIOException(other); 1003 return false; // keep compiler happy 1004 } 1005 return thisAttrs.isSameFile(otherAttrs); 1006 } 1007 1008 @Override 1009 public Path createSymbolicLink(Path obj, FileAttribute<?>... attrs) 1010 throws IOException 1011 { 1012 UnixPath target = checkPath(obj); 1013 1014 // no attributes supported when creating links 1015 if (attrs.length > 0) { 1016 UnixFileModeAttribute.toUnixMode(0, attrs); // may throw NPE or UOE 1017 throw new UnsupportedOperationException("Initial file attributes" + 1018 "not supported when creating symbolic link"); 1019 } 1020 1021 // permission check 1022 SecurityManager sm = System.getSecurityManager(); 1023 if (sm != null) { 1024 sm.checkPermission(new LinkPermission("symbolic")); 1025 checkWrite(); 1026 } 1027 1028 // create link 1029 try { 1030 symlink(target.asByteArray(), this); 1031 } catch (UnixException x) { 1032 x.rethrowAsIOException(this); 1033 } 1034 1035 return this; 1036 } 1037 1038 @Override 1039 public Path createLink(Path obj) throws IOException { 1040 UnixPath existing = checkPath(obj); 1041 1042 // permission check 1043 SecurityManager sm = System.getSecurityManager(); 1044 if (sm != null) { 1045 sm.checkPermission(new LinkPermission("hard")); 1046 this.checkWrite(); 1047 existing.checkWrite(); 1048 } 1049 try { 1050 link(existing, this); 1051 } catch (UnixException x) { 1052 x.rethrowAsIOException(this, existing); 1053 } 1054 return this; 1055 } 1056 1057 @Override 1058 public Path readSymbolicLink() throws IOException { 1059 // permission check 1060 SecurityManager sm = System.getSecurityManager(); 1061 if (sm != null) { 1062 FilePermission perm = new FilePermission(getPathForPermissionCheck(), 1063 SecurityConstants.FILE_READLINK_ACTION); 1064 AccessController.checkPermission(perm); 1065 } 1066 try { 1067 byte[] target = readlink(this); 1068 return new UnixPath(getFileSystem(), target); 1069 } catch (UnixException x) { 1070 if (x.errno() == UnixConstants.EINVAL) 1071 throw new NotLinkException(getPathForExecptionMessage()); 1072 x.rethrowAsIOException(this); 1073 return null; // keep compiler happy 1074 } 1075 } 1076 1077 @Override 1078 public UnixPath toAbsolutePath() { 1079 if (isAbsolute()) { 1080 return this; 1081 } 1082 // The path is relative so need to resolve against default directory, 1083 // taking care not to reveal the user.dir 1084 SecurityManager sm = System.getSecurityManager(); 1085 if (sm != null) { 1086 sm.checkPropertyAccess("user.dir"); 1087 } 1088 return new UnixPath(getFileSystem(), 1089 resolve(getFileSystem().defaultDirectory(), path)); 1090 } 1091 1092 @Override 1093 public UnixPath toRealPath(boolean resolveLinks) throws IOException { 1094 checkRead(); 1095 1096 UnixPath absolute = toAbsolutePath(); 1097 1098 // if resolveLinks is true then use realpath 1099 if (resolveLinks) { 1100 try { 1101 byte[] rp = realpath(absolute); 1102 return new UnixPath(getFileSystem(), rp); 1103 } catch (UnixException x) { 1104 x.rethrowAsIOException(this); 1105 } 1106 } 1107 1108 // if resolveLinks is false then eliminate "." and also ".." 1109 // where the previous element is not a link. 1110 UnixPath root = getFileSystem().rootDirectory(); 1111 UnixPath result = root; 1112 for (int i=0; i<absolute.getNameCount(); i++) { 1113 UnixPath element = absolute.getName(i); 1114 1115 // eliminate "." 1116 if ((element.asByteArray().length == 1) && (element.asByteArray()[0] == '.')) 1117 continue; 1118 1119 // cannot eliminate ".." if previous element is a link 1120 if ((element.asByteArray().length == 2) && (element.asByteArray()[0] == '.') && 1121 (element.asByteArray()[1] == '.')) 1122 { 1123 UnixFileAttributes attrs = null; 1124 try { 1125 attrs = UnixFileAttributes.get(result, false); 1126 } catch (UnixException x) { 1127 x.rethrowAsIOException(result); 1128 } 1129 if (!attrs.isSymbolicLink()) { 1130 result = result.getParent(); 1131 if (result == null) { 1132 result = root; 1133 } 1134 continue; 1135 } 1136 } 1137 result = result.resolve(element); 1138 } 1139 return result; 1140 } 1141 1142 @Override 1143 public boolean isHidden() { 1144 checkRead(); 1145 UnixPath name = getName(); 1146 if (name == null) 1147 return false; 1148 return (name.asByteArray()[0] == '.'); 1149 } 1150 1151 @Override 1152 public URI toUri() { 1153 return UnixUriUtils.toUri(this); 1154 } 1155 1156 @Override 1157 public WatchKey register(WatchService watcher, 1158 WatchEvent.Kind<?>[] events, 1159 WatchEvent.Modifier... modifiers) 1160 throws IOException 1161 { 1162 if (watcher == null) 1163 throw new NullPointerException(); 1164 if (!(watcher instanceof AbstractWatchService)) 1165 throw new ProviderMismatchException(); 1166 checkRead(); 1167 return ((AbstractWatchService)watcher).register(this, events, modifiers); 1168 } 1169 }