1 /* 2 * Copyright (c) 2008, 2010, 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 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 int thisLen = path.length; 628 int thatLen = that.path.length; 629 630 // other path is longer 631 if (thatLen > thisLen) 632 return false; 633 634 // other path is absolute so this path must be absolute 635 if (that.isAbsolute() && !this.isAbsolute()) 636 return false; 637 638 int thisOffsetCount = getNameCount(); 639 int thatOffsetCount = that.getNameCount(); 640 641 // given path has more elements that this path 642 if (thatOffsetCount > thisOffsetCount) { 643 return false; 644 } else { 645 // same number of elements 646 if (thatOffsetCount == thisOffsetCount) { 647 if (thisOffsetCount == 0) 648 return true; 649 int expectedLen = thisLen; 650 if (this.isAbsolute() && !that.isAbsolute()) 651 expectedLen--; 652 if (thatLen != expectedLen) 653 return false; 654 } else { 655 // this path has more elements so given path must be relative 656 if (that.isAbsolute()) 657 return false; 658 } 659 } 660 661 // compare bytes 662 int thisPos = offsets[thisOffsetCount - thatOffsetCount]; 663 int thatPos = that.offsets[0]; 664 if ((thatLen - thatPos) != (thisLen - thisPos)) 665 return false; 666 while (thatPos < thatLen) { 667 if (this.path[thisPos++] != that.path[thatPos++]) 668 return false; 669 } 670 671 return true; 672 } 673 674 @Override 675 public int compareTo(Path other) { 676 int len1 = path.length; 677 int len2 = ((UnixPath) other).path.length; 678 679 int n = Math.min(len1, len2); 680 byte v1[] = path; 681 byte v2[] = ((UnixPath) other).path; 682 683 int k = 0; 684 while (k < n) { 685 int c1 = v1[k] & 0xff; 686 int c2 = v2[k] & 0xff; 687 if (c1 != c2) { 688 return c1 - c2; 689 } 690 k++; 691 } 692 return len1 - len2; 693 } 694 695 @Override 696 public boolean equals(Object ob) { 697 if ((ob != null) && (ob instanceof UnixPath)) { 698 return compareTo((Path)ob) == 0; 699 } 700 return false; 701 } 702 703 @Override 704 public int hashCode() { 705 // OK if two or more threads compute hash 706 int h = hash; 707 if (h == 0) { 708 for (int i = 0; i< path.length; i++) { 709 h = 31*h + (path[i] & 0xff); 710 } 711 hash = h; 712 } 713 return h; 714 } 715 716 @Override 717 public String toString() { 718 // OK if two or more threads create a String 719 if (stringValue == null) 720 stringValue = new String(path); // platform encoding 721 return stringValue; 722 } 723 724 @Override 725 public Iterator<Path> iterator() { 726 initOffsets(); 727 return new Iterator<Path>() { 728 int i = 0; 729 @Override 730 public boolean hasNext() { 731 return (i < offsets.length); 732 } 733 @Override 734 public Path next() { 735 if (i < offsets.length) { 736 Path result = getName(i); 737 i++; 738 return result; 739 } else { 740 throw new NoSuchElementException(); 741 } 742 } 743 @Override 744 public void remove() { 745 throw new UnsupportedOperationException(); 746 } 747 }; 748 } 749 750 // -- file operations -- 751 752 // package-private 753 int openForAttributeAccess(boolean followLinks) throws IOException { 754 int flags = O_RDONLY; 755 if (!followLinks) 756 flags |= O_NOFOLLOW; 757 try { 758 return open(this, flags, 0); 759 } catch (UnixException x) { 760 // HACK: EINVAL instead of ELOOP on Solaris 10 prior to u4 (see 6460380) 761 if (getFileSystem().isSolaris() && x.errno() == EINVAL) 762 x.setError(ELOOP); 763 764 if (x.errno() == ELOOP) 765 throw new FileSystemException(getPathForExecptionMessage(), null, 766 x.getMessage() + " or unable to access attributes of symbolic link"); 767 768 x.rethrowAsIOException(this); 769 return -1; // keep compile happy 770 } 771 } 772 773 774 void checkRead() { 775 SecurityManager sm = System.getSecurityManager(); 776 if (sm != null) 777 sm.checkRead(getPathForPermissionCheck()); 778 } 779 780 void checkWrite() { 781 SecurityManager sm = System.getSecurityManager(); 782 if (sm != null) 783 sm.checkWrite(getPathForPermissionCheck()); 784 } 785 786 void checkDelete() { 787 SecurityManager sm = System.getSecurityManager(); 788 if (sm != null) 789 sm.checkDelete(getPathForPermissionCheck()); 790 } 791 792 @Override 793 public FileStore getFileStore() 794 throws IOException 795 { 796 SecurityManager sm = System.getSecurityManager(); 797 if (sm != null) { 798 sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); 799 checkRead(); 800 } 801 return getFileSystem().getFileStore(this); 802 } 803 804 @Override 805 public void checkAccess(AccessMode... modes) throws IOException { 806 boolean e = false; 807 boolean r = false; 808 boolean w = false; 809 boolean x = false; 810 811 if (modes.length == 0) { 812 e = true; 813 } else { 814 for (AccessMode mode: modes) { 815 switch (mode) { 816 case READ : r = true; break; 817 case WRITE : w = true; break; 818 case EXECUTE : x = true; break; 819 default: throw new AssertionError("Should not get here"); 820 } 821 } 822 } 823 824 int mode = 0; 825 if (e || r) { 826 checkRead(); 827 mode |= (r) ? R_OK : F_OK; 828 } 829 if (w) { 830 checkWrite(); 831 mode |= W_OK; 832 } 833 if (x) { 834 SecurityManager sm = System.getSecurityManager(); 835 if (sm != null) { 836 // not cached 837 sm.checkExec(getPathForPermissionCheck()); 838 } 839 mode |= X_OK; 840 } 841 try { 842 access(this, mode); 843 } catch (UnixException exc) { 844 exc.rethrowAsIOException(this); 845 } 846 } 847 848 @Override 849 void implDelete(boolean failIfNotExists) throws IOException { 850 checkDelete(); 851 852 // need file attributes to know if file is directory 853 UnixFileAttributes attrs = null; 854 try { 855 attrs = UnixFileAttributes.get(this, false); 856 if (attrs.isDirectory()) { 857 rmdir(this); 858 } else { 859 unlink(this); 860 } 861 } catch (UnixException x) { 862 // no-op if file does not exist 863 if (!failIfNotExists && x.errno() == ENOENT) 864 return; 865 866 // DirectoryNotEmptyException if not empty 867 if (attrs != null && attrs.isDirectory() && 868 (x.errno() == EEXIST || x.errno() == ENOTEMPTY)) 869 throw new DirectoryNotEmptyException(getPathForExecptionMessage()); 870 871 x.rethrowAsIOException(this); 872 } 873 } 874 875 @Override 876 public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter) 877 throws IOException 878 { 879 if (filter == null) 880 throw new NullPointerException(); 881 checkRead(); 882 883 // can't return SecureDirectoryStream on kernels that don't support 884 // openat, etc. 885 if (!supportsAtSysCalls()) { 886 try { 887 long ptr = opendir(this); 888 return new UnixDirectoryStream(this, ptr, filter); 889 } catch (UnixException x) { 890 if (x.errno() == ENOTDIR) 891 throw new NotDirectoryException(getPathForExecptionMessage()); 892 x.rethrowAsIOException(this); 893 } 894 } 895 896 // open directory and dup file descriptor for use by 897 // opendir/readdir/closedir 898 int dfd1 = -1; 899 int dfd2 = -1; 900 long dp = 0L; 901 try { 902 dfd1 = open(this, O_RDONLY, 0); 903 dfd2 = dup(dfd1); 904 dp = fdopendir(dfd1); 905 } catch (UnixException x) { 906 if (dfd1 != -1) 907 close(dfd1); 908 if (dfd2 != -1) 909 close(dfd2); 910 if (x.errno() == UnixConstants.ENOTDIR) 911 throw new NotDirectoryException(getPathForExecptionMessage()); 912 x.rethrowAsIOException(this); 913 } 914 return new UnixSecureDirectoryStream(this, dp, dfd2, filter); 915 } 916 917 // invoked by AbstractPath#copyTo 918 @Override 919 public void implCopyTo(Path obj, CopyOption... options) 920 throws IOException 921 { 922 UnixPath target = (UnixPath)obj; 923 UnixCopyFile.copy(this, target, options); 924 } 925 926 @Override 927 public void implMoveTo(Path obj, CopyOption... options) 928 throws IOException 929 { 930 UnixPath target = (UnixPath)obj; 931 UnixCopyFile.move(this, target, options); 932 } 933 934 @Override 935 @SuppressWarnings("unchecked") 936 public <V extends FileAttributeView> V 937 getFileAttributeView(Class<V> type, LinkOption... options) 938 { 939 FileAttributeView view = getFileSystem() 940 .newFileAttributeView(type, this, options); 941 if (view == null) 942 return null; 943 return (V) view; 944 } 945 946 @Override 947 public DynamicFileAttributeView getFileAttributeView(String name, 948 LinkOption... options) 949 { 950 return getFileSystem().newFileAttributeView(name, this, options); 951 } 952 953 @Override 954 public Path createDirectory(FileAttribute<?>... attrs) 955 throws IOException 956 { 957 checkWrite(); 958 959 int mode = UnixFileModeAttribute 960 .toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs); 961 try { 962 mkdir(this, mode); 963 } catch (UnixException x) { 964 x.rethrowAsIOException(this); 965 } 966 return this; 967 } 968 969 @Override 970 public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options, 971 FileAttribute<?>... attrs) 972 throws IOException 973 { 974 int mode = UnixFileModeAttribute 975 .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs); 976 try { 977 return UnixChannelFactory.newFileChannel(this, options, mode); 978 } catch (UnixException x) { 979 x.rethrowAsIOException(this); 980 return null; // keep compiler happy 981 } 982 } 983 984 @Override 985 public boolean isSameFile(Path obj) throws IOException { 986 if (this.equals(obj)) 987 return true; 988 if (!(obj instanceof UnixPath)) // includes null check 989 return false; 990 UnixPath other = (UnixPath)obj; 991 992 // check security manager access to both files 993 this.checkRead(); 994 other.checkRead(); 995 996 UnixFileAttributes thisAttrs; 997 UnixFileAttributes otherAttrs; 998 try { 999 thisAttrs = UnixFileAttributes.get(this, true); 1000 } catch (UnixException x) { 1001 x.rethrowAsIOException(this); 1002 return false; // keep compiler happy 1003 } 1004 try { 1005 otherAttrs = UnixFileAttributes.get(other, true); 1006 } catch (UnixException x) { 1007 x.rethrowAsIOException(other); 1008 return false; // keep compiler happy 1009 } 1010 return thisAttrs.isSameFile(otherAttrs); 1011 } 1012 1013 @Override 1014 public Path createSymbolicLink(Path obj, FileAttribute<?>... attrs) 1015 throws IOException 1016 { 1017 UnixPath target = checkPath(obj); 1018 1019 // no attributes supported when creating links 1020 if (attrs.length > 0) { 1021 UnixFileModeAttribute.toUnixMode(0, attrs); // may throw NPE or UOE 1022 throw new UnsupportedOperationException("Initial file attributes" + 1023 "not supported when creating symbolic link"); 1024 } 1025 1026 // permission check 1027 SecurityManager sm = System.getSecurityManager(); 1028 if (sm != null) { 1029 sm.checkPermission(new LinkPermission("symbolic")); 1030 checkWrite(); 1031 } 1032 1033 // create link 1034 try { 1035 symlink(target.asByteArray(), this); 1036 } catch (UnixException x) { 1037 x.rethrowAsIOException(this); 1038 } 1039 1040 return this; 1041 } 1042 1043 @Override 1044 public Path createLink(Path obj) throws IOException { 1045 UnixPath existing = checkPath(obj); 1046 1047 // permission check 1048 SecurityManager sm = System.getSecurityManager(); 1049 if (sm != null) { 1050 sm.checkPermission(new LinkPermission("hard")); 1051 this.checkWrite(); 1052 existing.checkWrite(); 1053 } 1054 try { 1055 link(existing, this); 1056 } catch (UnixException x) { 1057 x.rethrowAsIOException(this, existing); 1058 } 1059 return this; 1060 } 1061 1062 @Override 1063 public Path readSymbolicLink() throws IOException { 1064 // permission check 1065 SecurityManager sm = System.getSecurityManager(); 1066 if (sm != null) { 1067 FilePermission perm = new FilePermission(getPathForPermissionCheck(), 1068 SecurityConstants.FILE_READLINK_ACTION); 1069 AccessController.checkPermission(perm); 1070 } 1071 try { 1072 byte[] target = readlink(this); 1073 return new UnixPath(getFileSystem(), target); 1074 } catch (UnixException x) { 1075 if (x.errno() == UnixConstants.EINVAL) 1076 throw new NotLinkException(getPathForExecptionMessage()); 1077 x.rethrowAsIOException(this); 1078 return null; // keep compiler happy 1079 } 1080 } 1081 1082 @Override 1083 public UnixPath toAbsolutePath() { 1084 if (isAbsolute()) { 1085 return this; 1086 } 1087 // The path is relative so need to resolve against default directory, 1088 // taking care not to reveal the user.dir 1089 SecurityManager sm = System.getSecurityManager(); 1090 if (sm != null) { 1091 sm.checkPropertyAccess("user.dir"); 1092 } 1093 return new UnixPath(getFileSystem(), 1094 resolve(getFileSystem().defaultDirectory(), path)); 1095 } 1096 1097 @Override 1098 public UnixPath toRealPath(boolean resolveLinks) throws IOException { 1099 checkRead(); 1100 1101 UnixPath absolute = toAbsolutePath(); 1102 1103 // if resolveLinks is true then use realpath 1104 if (resolveLinks) { 1105 try { 1106 byte[] rp = realpath(absolute); 1107 return new UnixPath(getFileSystem(), rp); 1108 } catch (UnixException x) { 1109 x.rethrowAsIOException(this); 1110 } 1111 } 1112 1113 // if resolveLinks is false then eliminate "." and also ".." 1114 // where the previous element is not a link. 1115 UnixPath root = getFileSystem().rootDirectory(); 1116 UnixPath result = root; 1117 for (int i=0; i<absolute.getNameCount(); i++) { 1118 UnixPath element = absolute.getName(i); 1119 1120 // eliminate "." 1121 if ((element.asByteArray().length == 1) && (element.asByteArray()[0] == '.')) 1122 continue; 1123 1124 // cannot eliminate ".." if previous element is a link 1125 if ((element.asByteArray().length == 2) && (element.asByteArray()[0] == '.') && 1126 (element.asByteArray()[1] == '.')) 1127 { 1128 UnixFileAttributes attrs = null; 1129 try { 1130 attrs = UnixFileAttributes.get(result, false); 1131 } catch (UnixException x) { 1132 x.rethrowAsIOException(result); 1133 } 1134 if (!attrs.isSymbolicLink()) { 1135 result = result.getParent(); 1136 if (result == null) { 1137 result = root; 1138 } 1139 continue; 1140 } 1141 } 1142 result = result.resolve(element); 1143 } 1144 1145 // check file exists (without following links) 1146 try { 1147 UnixFileAttributes.get(result, false); 1148 } catch (UnixException x) { 1149 x.rethrowAsIOException(result); 1150 } 1151 return result; 1152 } 1153 1154 @Override 1155 public boolean isHidden() { 1156 checkRead(); 1157 UnixPath name = getName(); 1158 if (name == null) 1159 return false; 1160 return (name.asByteArray()[0] == '.'); 1161 } 1162 1163 @Override 1164 public URI toUri() { 1165 return UnixUriUtils.toUri(this); 1166 } 1167 1168 @Override 1169 public WatchKey register(WatchService watcher, 1170 WatchEvent.Kind<?>[] events, 1171 WatchEvent.Modifier... modifiers) 1172 throws IOException 1173 { 1174 if (watcher == null) 1175 throw new NullPointerException(); 1176 if (!(watcher instanceof AbstractWatchService)) 1177 throw new ProviderMismatchException(); 1178 checkRead(); 1179 return ((AbstractWatchService)watcher).register(this, events, modifiers); 1180 } 1181 }