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.file.*; 29 import java.nio.file.attribute.*; 30 import java.nio.channels.*; 31 import java.io.*; 32 import java.net.URI; 33 import java.security.AccessController; 34 import java.util.*; 35 import java.lang.ref.WeakReference; 36 37 import com.sun.nio.file.ExtendedWatchEventModifier; 38 39 import sun.security.util.SecurityConstants; 40 import sun.misc.Unsafe; 41 42 import static sun.nio.fs.WindowsNativeDispatcher.*; 43 import static sun.nio.fs.WindowsConstants.*; 44 45 /** 46 * Windows implementation of Path 47 */ 48 49 class WindowsPath extends AbstractPath { 50 private static final Unsafe unsafe = Unsafe.getUnsafe(); 51 52 // The maximum path that does not require long path prefix. On Windows 53 // the maximum path is 260 minus 1 (NUL) but for directories it is 260 54 // minus 12 minus 1 (to allow for the creation of a 8.3 file in the 55 // directory). 56 private static final int MAX_PATH = 247; 57 58 // Maximum extended-length path 59 private static final int MAX_LONG_PATH = 32000; 60 61 // FIXME - eliminate this reference to reduce space 62 private final WindowsFileSystem fs; 63 64 // path type 65 private final WindowsPathType type; 66 // root component (may be empty) 67 private final String root; 68 // normalized path 69 private final String path; 70 71 // the path to use in Win32 calls. This differs from path for relative 72 // paths and has a long path prefix for all paths longer than MAX_PATH. 73 private volatile WeakReference<String> pathForWin32Calls; 74 75 // offsets into name components (computed lazily) 76 private volatile Integer[] offsets; 77 78 // computed hash code (computed lazily, no need to be volatile) 79 private int hash; 80 81 82 /** 83 * Initializes a new instance of this class. 84 */ 85 private WindowsPath(WindowsFileSystem fs, 86 WindowsPathType type, 87 String root, 88 String path) 89 { 90 this.fs = fs; 91 this.type = type; 92 this.root = root; 93 this.path = path; 94 } 95 96 /** 97 * Creates a Path by parsing the given path. 98 */ 99 static WindowsPath parse(WindowsFileSystem fs, String path) { 100 WindowsPathParser.Result result = WindowsPathParser.parse(path); 101 return new WindowsPath(fs, result.type(), result.root(), result.path()); 102 } 103 104 /** 105 * Creates a Path from a given path that is known to be normalized. 106 */ 107 static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, 108 String path, 109 BasicFileAttributes attrs) 110 { 111 try { 112 WindowsPathParser.Result result = 113 WindowsPathParser.parseNormalizedPath(path); 114 if (attrs == null) { 115 return new WindowsPath(fs, 116 result.type(), 117 result.root(), 118 result.path()); 119 } else { 120 return new WindowsPathWithAttributes(fs, 121 result.type(), 122 result.root(), 123 result.path(), 124 attrs); 125 } 126 } catch (InvalidPathException x) { 127 throw new AssertionError(x.getMessage()); 128 } 129 } 130 131 /** 132 * Creates a WindowsPath from a given path that is known to be normalized. 133 */ 134 static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, 135 String path) 136 { 137 return createFromNormalizedPath(fs, path, null); 138 } 139 140 /** 141 * Special implementation with attached/cached attributes (used to quicken 142 * file tree traveral) 143 */ 144 private static class WindowsPathWithAttributes 145 extends WindowsPath implements BasicFileAttributesHolder 146 { 147 final WeakReference<BasicFileAttributes> ref; 148 149 WindowsPathWithAttributes(WindowsFileSystem fs, 150 WindowsPathType type, 151 String root, 152 String path, 153 BasicFileAttributes attrs) 154 { 155 super(fs, type, root, path); 156 ref = new WeakReference<BasicFileAttributes>(attrs); 157 } 158 159 @Override 160 public BasicFileAttributes get() { 161 return ref.get(); 162 } 163 164 @Override 165 public void invalidate() { 166 ref.clear(); 167 } 168 169 // no need to override equals/hashCode. 170 } 171 172 // use this message when throwing exceptions 173 String getPathForExceptionMessage() { 174 return path; 175 } 176 177 // use this path for permission checks 178 String getPathForPermissionCheck() { 179 return path; 180 } 181 182 // use this path for Win32 calls 183 // This method will prefix long paths with \\?\ or \\?\UNC as required. 184 String getPathForWin32Calls() throws WindowsException { 185 // short absolute paths can be used directly 186 if (isAbsolute() && path.length() <= MAX_PATH) 187 return path; 188 189 // return cached values if available 190 WeakReference<String> ref = pathForWin32Calls; 191 String resolved = (ref != null) ? ref.get() : null; 192 if (resolved != null) { 193 // Win32 path already available 194 return resolved; 195 } 196 197 // resolve against default directory 198 resolved = getAbsolutePath(); 199 200 // Long paths need to have "." and ".." removed and be prefixed with 201 // "\\?\". Note that it is okay to remove ".." even when it follows 202 // a link - for example, it is okay for foo/link/../bar to be changed 203 // to foo/bar. The reason is that Win32 APIs to access foo/link/../bar 204 // will access foo/bar anyway (which differs to Unix systems) 205 if (resolved.length() > MAX_PATH) { 206 if (resolved.length() > MAX_LONG_PATH) { 207 throw new WindowsException("Cannot access file with path exceeding " 208 + MAX_LONG_PATH + " characters"); 209 } 210 resolved = addPrefixIfNeeded(GetFullPathName(resolved)); 211 } 212 213 // cache the resolved path (except drive relative paths as the working 214 // directory on removal media devices can change during the lifetime 215 // of the VM) 216 if (type != WindowsPathType.DRIVE_RELATIVE) { 217 synchronized (path) { 218 pathForWin32Calls = new WeakReference<String>(resolved); 219 } 220 } 221 return resolved; 222 } 223 224 // return this path resolved against the file system's default directory 225 private String getAbsolutePath() throws WindowsException { 226 if (isAbsolute()) 227 return path; 228 229 // Relative path ("foo" for example) 230 if (type == WindowsPathType.RELATIVE) { 231 String defaultDirectory = getFileSystem().defaultDirectory(); 232 if (defaultDirectory.endsWith("\\")) { 233 return defaultDirectory + path; 234 } else { 235 StringBuilder sb = 236 new StringBuilder(defaultDirectory.length() + path.length() + 1); 237 return sb.append(defaultDirectory).append('\\').append(path).toString(); 238 } 239 } 240 241 // Directory relative path ("\foo" for example) 242 if (type == WindowsPathType.DIRECTORY_RELATIVE) { 243 String defaultRoot = getFileSystem().defaultRoot(); 244 return defaultRoot + path.substring(1); 245 } 246 247 // Drive relative path ("C:foo" for example). 248 if (isSameDrive(root, getFileSystem().defaultRoot())) { 249 // relative to default directory 250 String remaining = path.substring(root.length()); 251 String defaultDirectory = getFileSystem().defaultDirectory(); 252 String result; 253 if (defaultDirectory.endsWith("\\")) { 254 result = defaultDirectory + remaining; 255 } else { 256 result = defaultDirectory + "\\" + remaining; 257 } 258 return result; 259 } else { 260 // relative to some other drive 261 String wd; 262 try { 263 int dt = GetDriveType(root + "\\"); 264 if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR) 265 throw new WindowsException(""); 266 wd = GetFullPathName(root + "."); 267 } catch (WindowsException x) { 268 throw new WindowsException("Unable to get working directory of drive '" + 269 Character.toUpperCase(root.charAt(0)) + "'"); 270 } 271 String result = wd; 272 if (wd.endsWith("\\")) { 273 result += path.substring(root.length()); 274 } else { 275 if (path.length() > root.length()) 276 result += "\\" + path.substring(root.length()); 277 } 278 return result; 279 } 280 } 281 282 // returns true if same drive letter 283 private static boolean isSameDrive(String root1, String root2) { 284 return Character.toUpperCase(root1.charAt(0)) == 285 Character.toUpperCase(root2.charAt(0)); 286 } 287 288 // Add long path prefix to path if required 289 private static String addPrefixIfNeeded(String path) { 290 if (path.length() > 248) { 291 if (path.startsWith("\\\\")) { 292 path = "\\\\?\\UNC" + path.substring(1, path.length()); 293 } else { 294 path = "\\\\?\\" + path; 295 } 296 } 297 return path; 298 } 299 300 @Override 301 public WindowsFileSystem getFileSystem() { 302 return fs; 303 } 304 305 // -- Path operations -- 306 307 @Override 308 public Path getName() { 309 // represents root component only 310 if (root.length() == path.length()) 311 return null; 312 int off = path.lastIndexOf('\\'); 313 if (off < root.length()) 314 off = root.length(); 315 else 316 off++; 317 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off)); 318 } 319 320 @Override 321 public WindowsPath getParent() { 322 // represents root component only 323 if (root.length() == path.length()) 324 return null; 325 int off = path.lastIndexOf('\\'); 326 if (off < root.length()) 327 return getRoot(); 328 else 329 return new WindowsPath(getFileSystem(), 330 type, 331 root, 332 path.substring(0, off)); 333 } 334 335 @Override 336 public WindowsPath getRoot() { 337 if (root.length() == 0) 338 return null; 339 return new WindowsPath(getFileSystem(), type, root, root); 340 } 341 342 // package-private 343 boolean isUnc() { 344 return type == WindowsPathType.UNC; 345 } 346 347 boolean needsSlashWhenResolving() { 348 if (path.endsWith("\\")) 349 return false; 350 return path.length() > root.length(); 351 } 352 353 @Override 354 public boolean isAbsolute() { 355 return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC; 356 } 357 358 private WindowsPath checkPath(FileRef path) { 359 if (path == null) 360 throw new NullPointerException(); 361 if (!(path instanceof WindowsPath)) { 362 throw new ProviderMismatchException(); 363 } 364 return (WindowsPath)path; 365 } 366 367 @Override 368 public WindowsPath relativize(Path obj) { 369 WindowsPath other = checkPath(obj); 370 if (this.equals(other)) 371 return null; 372 373 // can only relativize paths of the same type 374 if (this.type != other.type) 375 throw new IllegalArgumentException("'other' is different type of Path"); 376 377 // can only relativize paths if root component matches 378 if (!this.root.equalsIgnoreCase(other.root)) 379 throw new IllegalArgumentException("'other' has different root"); 380 381 int bn = this.getNameCount(); 382 int cn = other.getNameCount(); 383 384 // skip matching names 385 int n = (bn > cn) ? cn : bn; 386 int i = 0; 387 while (i < n) { 388 if (!this.getName(i).equals(other.getName(i))) 389 break; 390 i++; 391 } 392 393 // append ..\ for remaining names in the base 394 StringBuilder result = new StringBuilder(); 395 for (int j=i; j<bn; j++) { 396 result.append("..\\"); 397 } 398 399 // append remaining names in child 400 for (int j=i; j<cn; j++) { 401 result.append(other.getName(j).toString()); 402 result.append("\\"); 403 } 404 405 // drop trailing slash in result 406 result.setLength(result.length()-1); 407 return createFromNormalizedPath(getFileSystem(), result.toString()); 408 } 409 410 @Override 411 public Path normalize() { 412 final int count = getNameCount(); 413 if (count == 0) 414 return this; 415 416 boolean[] ignore = new boolean[count]; // true => ignore name 417 int remaining = count; // number of names remaining 418 419 // multiple passes to eliminate all occurences of "." and "name/.." 420 int prevRemaining; 421 do { 422 prevRemaining = remaining; 423 int prevName = -1; 424 for (int i=0; i<count; i++) { 425 if (ignore[i]) 426 continue; 427 428 String name = elementAsString(i); 429 430 // not "." or ".." 431 if (name.length() > 2) { 432 prevName = i; 433 continue; 434 } 435 436 // "." or something else 437 if (name.length() == 1) { 438 // ignore "." 439 if (name.charAt(0) == '.') { 440 ignore[i] = true; 441 remaining--; 442 } else { 443 prevName = i; 444 } 445 continue; 446 } 447 448 // not ".." 449 if (name.charAt(0) != '.' || name.charAt(1) != '.') { 450 prevName = i; 451 continue; 452 } 453 454 // ".." found 455 if (prevName >= 0) { 456 // name/<ignored>/.. found so mark name and ".." to be 457 // ignored 458 ignore[prevName] = true; 459 ignore[i] = true; 460 remaining = remaining - 2; 461 prevName = -1; 462 } else { 463 // Cases: 464 // C:\<ignored>\.. 465 // \\server\\share\<ignored>\.. 466 // \<ignored>.. 467 if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) { 468 boolean hasPrevious = false; 469 for (int j=0; j<i; j++) { 470 if (!ignore[j]) { 471 hasPrevious = true; 472 break; 473 } 474 } 475 if (!hasPrevious) { 476 // all proceeding names are ignored 477 ignore[i] = true; 478 remaining--; 479 } 480 } 481 } 482 } 483 } while (prevRemaining > remaining); 484 485 // no redundant names 486 if (remaining == count) 487 return this; 488 489 // corner case - all names removed 490 if (remaining == 0) { 491 return getRoot(); 492 } 493 494 // re-constitute the path from the remaining names. 495 StringBuilder result = new StringBuilder(); 496 if (root != null) 497 result.append(root); 498 for (int i=0; i<count; i++) { 499 if (!ignore[i]) { 500 result.append(getName(i).toString()); 501 result.append("\\"); 502 } 503 } 504 505 // drop trailing slash in result 506 result.setLength(result.length()-1); 507 return createFromNormalizedPath(getFileSystem(), result.toString()); 508 } 509 510 @Override 511 public WindowsPath resolve(Path obj) { 512 if (obj == null) 513 return this; 514 WindowsPath other = checkPath(obj); 515 if (other.isAbsolute()) 516 return other; 517 518 switch (other.type) { 519 case RELATIVE: { 520 String result; 521 if (path.endsWith("\\") || (root.length() == path.length())) { 522 result = path + other.path; 523 } else { 524 result = path + "\\" + other.path; 525 } 526 return new WindowsPath(getFileSystem(), type, root, result); 527 } 528 529 case DIRECTORY_RELATIVE: { 530 String result; 531 if (root.endsWith("\\")) { 532 result = root + other.path.substring(1); 533 } else { 534 result = root + other.path; 535 } 536 return createFromNormalizedPath(getFileSystem(), result); 537 } 538 539 case DRIVE_RELATIVE: { 540 if (!root.endsWith("\\")) 541 return other; 542 // if different roots then return other 543 String thisRoot = root.substring(0, root.length()-1); 544 if (!thisRoot.equalsIgnoreCase(other.root)) 545 return other; 546 // same roots 547 String remaining = other.path.substring(other.root.length()); 548 String result; 549 if (path.endsWith("\\")) { 550 result = path + remaining; 551 } else { 552 result = path + "\\" + remaining; 553 } 554 return createFromNormalizedPath(getFileSystem(), result); 555 } 556 557 default: 558 throw new AssertionError(); 559 } 560 } 561 562 @Override 563 public WindowsPath resolve(String other) { 564 return resolve(getFileSystem().getPath(other)); 565 } 566 567 // generate offset array 568 private void initOffsets() { 569 if (offsets == null) { 570 ArrayList<Integer> list = new ArrayList<Integer>(); 571 int start = root.length(); 572 int off = root.length(); 573 while (off < path.length()) { 574 if (path.charAt(off) != '\\') { 575 off++; 576 } else { 577 list.add(start); 578 start = ++off; 579 } 580 } 581 if (start != off) 582 list.add(start); 583 synchronized (this) { 584 if (offsets == null) 585 offsets = list.toArray(new Integer[list.size()]); 586 } 587 } 588 } 589 590 @Override 591 public int getNameCount() { 592 initOffsets(); 593 return offsets.length; 594 } 595 596 private String elementAsString(int i) { 597 initOffsets(); 598 if (i == (offsets.length-1)) 599 return path.substring(offsets[i]); 600 return path.substring(offsets[i], offsets[i+1]-1); 601 } 602 603 @Override 604 public WindowsPath getName(int index) { 605 initOffsets(); 606 if (index < 0 || index >= offsets.length) 607 throw new IllegalArgumentException(); 608 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index)); 609 } 610 611 @Override 612 public WindowsPath subpath(int beginIndex, int endIndex) { 613 initOffsets(); 614 if (beginIndex < 0) 615 throw new IllegalArgumentException(); 616 if (beginIndex >= offsets.length) 617 throw new IllegalArgumentException(); 618 if (endIndex > offsets.length) 619 throw new IllegalArgumentException(); 620 if (beginIndex >= endIndex) 621 throw new IllegalArgumentException(); 622 623 StringBuilder sb = new StringBuilder(); 624 Integer[] nelems = new Integer[endIndex - beginIndex]; 625 for (int i = beginIndex; i < endIndex; i++) { 626 nelems[i-beginIndex] = sb.length(); 627 sb.append(elementAsString(i)); 628 if (i != (endIndex-1)) 629 sb.append("\\"); 630 } 631 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString()); 632 } 633 634 @Override 635 public boolean startsWith(Path obj) { 636 WindowsPath other = checkPath(obj); 637 638 // if this path has a root component the given path's root must match 639 if (!this.root.equalsIgnoreCase(other.root)) 640 return false; 641 642 // roots match so compare elements 643 int thisCount = getNameCount(); 644 int otherCount = other.getNameCount(); 645 if (otherCount <= thisCount) { 646 while (--otherCount >= 0) { 647 String thisElement = this.elementAsString(otherCount); 648 String otherElement = other.elementAsString(otherCount); 649 // FIXME: should compare in uppercase 650 if (!thisElement.equalsIgnoreCase(otherElement)) 651 return false; 652 } 653 return true; 654 } 655 return false; 656 } 657 658 @Override 659 public boolean endsWith(Path obj) { 660 WindowsPath other = checkPath(obj); 661 662 // other path is longer 663 if (other.path.length() > path.length()) { 664 return false; 665 } 666 667 int thisCount = this.getNameCount(); 668 int otherCount = other.getNameCount(); 669 670 // given path has more elements that this path 671 if (otherCount > thisCount) { 672 return false; 673 } 674 675 // compare roots 676 if (other.root.length() > 0) { 677 if (otherCount < thisCount) 678 return false; 679 // FIXME: should compare in uppercase 680 if (!this.root.equalsIgnoreCase(other.root)) 681 return false; 682 } 683 684 // match last 'otherCount' elements 685 int off = thisCount - otherCount; 686 while (--otherCount >= 0) { 687 String thisElement = this.elementAsString(off + otherCount); 688 String otherElement = other.elementAsString(otherCount); 689 // FIXME: should compare in uppercase 690 if (!thisElement.equalsIgnoreCase(otherElement)) 691 return false; 692 } 693 return true; 694 } 695 696 @Override 697 public int compareTo(Path obj) { 698 if (obj == null) 699 throw new NullPointerException(); 700 String s1 = path; 701 String s2 = ((WindowsPath)obj).path; 702 int n1 = s1.length(); 703 int n2 = s2.length(); 704 int min = Math.min(n1, n2); 705 for (int i = 0; i < min; i++) { 706 char c1 = s1.charAt(i); 707 char c2 = s2.charAt(i); 708 if (c1 != c2) { 709 c1 = Character.toUpperCase(c1); 710 c2 = Character.toUpperCase(c2); 711 if (c1 != c2) { 712 return c1 - c2; 713 } 714 } 715 } 716 return n1 - n2; 717 } 718 719 @Override 720 public boolean equals(Object obj) { 721 if ((obj != null) && (obj instanceof WindowsPath)) { 722 return compareTo((Path)obj) == 0; 723 } 724 return false; 725 } 726 727 @Override 728 public int hashCode() { 729 // OK if two or more threads compute hash 730 int h = hash; 731 if (h == 0) { 732 for (int i = 0; i< path.length(); i++) { 733 h = 31*h + Character.toUpperCase(path.charAt(i)); 734 } 735 hash = h; 736 } 737 return h; 738 } 739 740 @Override 741 public String toString() { 742 return path; 743 } 744 745 @Override 746 public Iterator<Path> iterator() { 747 return new Iterator<Path>() { 748 private int i = 0; 749 @Override 750 public boolean hasNext() { 751 return (i < getNameCount()); 752 } 753 @Override 754 public Path next() { 755 if (i < getNameCount()) { 756 Path result = getName(i); 757 i++; 758 return result; 759 } else { 760 throw new NoSuchElementException(); 761 } 762 } 763 @Override 764 public void remove() { 765 throw new UnsupportedOperationException(); 766 } 767 }; 768 } 769 770 // -- file operations -- 771 772 // package-private 773 long openForReadAttributeAccess(boolean followLinks) 774 throws WindowsException 775 { 776 int flags = FILE_FLAG_BACKUP_SEMANTICS; 777 if (!followLinks && getFileSystem().supportsLinks()) 778 flags |= FILE_FLAG_OPEN_REPARSE_POINT; 779 return CreateFile(getPathForWin32Calls(), 780 FILE_READ_ATTRIBUTES, 781 (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 782 0L, 783 OPEN_EXISTING, 784 flags); 785 } 786 787 void checkRead() { 788 SecurityManager sm = System.getSecurityManager(); 789 if (sm != null) { 790 sm.checkRead(getPathForPermissionCheck()); 791 } 792 } 793 794 void checkWrite() { 795 SecurityManager sm = System.getSecurityManager(); 796 if (sm != null) { 797 sm.checkWrite(getPathForPermissionCheck()); 798 } 799 } 800 801 void checkDelete() { 802 SecurityManager sm = System.getSecurityManager(); 803 if (sm != null) { 804 sm.checkDelete(getPathForPermissionCheck()); 805 } 806 } 807 808 @Override 809 public FileStore getFileStore() 810 throws IOException 811 { 812 SecurityManager sm = System.getSecurityManager(); 813 if (sm != null) { 814 sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); 815 checkRead(); 816 } 817 return WindowsFileStore.create(this); 818 } 819 820 /** 821 * Returns buffer with SID_AND_ATTRIBUTES structure representing the user 822 * associated with the current thread access token. 823 * FIXME - this should be cached. 824 */ 825 private NativeBuffer getUserInfo() throws IOException { 826 try { 827 long hToken = WindowsSecurity.processTokenWithQueryAccess; 828 int size = GetTokenInformation(hToken, TokenUser, 0L, 0); 829 assert size > 0; 830 831 NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 832 try { 833 int newsize = GetTokenInformation(hToken, TokenUser, 834 buffer.address(), size); 835 if (newsize != size) 836 throw new AssertionError(); 837 return buffer; 838 } catch (WindowsException x) { 839 buffer.release(); 840 throw x; 841 } 842 } catch (WindowsException x) { 843 throw new IOException(x.getMessage()); 844 } 845 } 846 847 /** 848 * Reads the file ACL and return the effective access as ACCESS_MASK 849 */ 850 private int getEffectiveAccess() throws IOException { 851 // read security descriptor continaing ACL (symlinks are followed) 852 String target = WindowsLinkSupport.getFinalPath(this, true); 853 NativeBuffer aclBuffer = WindowsAclFileAttributeView 854 .getFileSecurity(target, DACL_SECURITY_INFORMATION); 855 856 // retrieves DACL from security descriptor 857 long pAcl = GetSecurityDescriptorDacl(aclBuffer.address()); 858 859 // Use GetEffectiveRightsFromAcl to get effective access to file 860 try { 861 NativeBuffer userBuffer = getUserInfo(); 862 try { 863 try { 864 // SID_AND_ATTRIBUTES->pSid 865 long pSid = unsafe.getAddress(userBuffer.address()); 866 long pTrustee = BuildTrusteeWithSid(pSid); 867 try { 868 return GetEffectiveRightsFromAcl(pAcl, pTrustee); 869 } finally { 870 LocalFree(pTrustee); 871 } 872 } catch (WindowsException x) { 873 throw new IOException("Unable to get effective rights from ACL: " + 874 x.getMessage()); 875 } 876 } finally { 877 userBuffer.release(); 878 } 879 } finally { 880 aclBuffer.release(); 881 } 882 } 883 884 @Override 885 public void checkAccess(AccessMode... modes) throws IOException { 886 // if no access modes then simply file attributes 887 if (modes.length == 0) { 888 checkRead(); 889 try { 890 WindowsFileAttributes.get(this, true); 891 } catch (WindowsException exc) { 892 exc.rethrowAsIOException(this); 893 } 894 return; 895 } 896 897 boolean r = false; 898 boolean w = false; 899 boolean x = false; 900 for (AccessMode mode: modes) { 901 switch (mode) { 902 case READ : r = true; break; 903 case WRITE : w = true; break; 904 case EXECUTE : x = true; break; 905 default: throw new AssertionError("Should not get here"); 906 } 907 } 908 909 int mask = 0; 910 if (r) { 911 checkRead(); 912 mask |= FILE_READ_DATA; 913 } 914 if (w) { 915 checkWrite(); 916 mask |= FILE_WRITE_DATA; 917 } 918 if (x) { 919 SecurityManager sm = System.getSecurityManager(); 920 if (sm != null) 921 sm.checkExec(getPathForPermissionCheck()); 922 mask |= FILE_EXECUTE; 923 } 924 925 if ((getEffectiveAccess() & mask) == 0) 926 throw new AccessDeniedException( 927 this.getPathForExceptionMessage(), null, 928 "Effective permissions does not allow requested access"); 929 930 // for write access we neeed to check if the DOS readonly attribute 931 // and if the volume is read-only 932 if (w) { 933 try { 934 WindowsFileAttributes attrs = WindowsFileAttributes.get(this, true); 935 if (!attrs.isDirectory() && attrs.isReadOnly()) 936 throw new AccessDeniedException( 937 this.getPathForExceptionMessage(), null, 938 "DOS readonly attribute is set"); 939 } catch (WindowsException exc) { 940 exc.rethrowAsIOException(this); 941 } 942 943 if (WindowsFileStore.create(this).isReadOnly()) { 944 throw new AccessDeniedException( 945 this.getPathForExceptionMessage(), null, "Read-only file system"); 946 } 947 return; 948 } 949 } 950 951 @Override 952 void implDelete(boolean failIfNotExists) throws IOException { 953 checkDelete(); 954 955 WindowsFileAttributes attrs = null; 956 try { 957 // need to know if file is a directory or junction 958 attrs = WindowsFileAttributes.get(this, false); 959 if (attrs.isDirectory() || attrs.isDirectoryLink()) { 960 RemoveDirectory(getPathForWin32Calls()); 961 } else { 962 DeleteFile(getPathForWin32Calls()); 963 } 964 } catch (WindowsException x) { 965 966 // no-op if file does not exist 967 if (!failIfNotExists && 968 (x.lastError() == ERROR_FILE_NOT_FOUND || 969 x.lastError() == ERROR_PATH_NOT_FOUND)) return; 970 971 if (attrs != null && attrs.isDirectory()) { 972 // ERROR_ALREADY_EXISTS is returned when attempting to delete 973 // non-empty directory on SAMBA servers. 974 if (x.lastError() == ERROR_DIR_NOT_EMPTY || 975 x.lastError() == ERROR_ALREADY_EXISTS) 976 { 977 throw new DirectoryNotEmptyException( 978 getPathForExceptionMessage()); 979 } 980 } 981 x.rethrowAsIOException(this); 982 } 983 } 984 985 @Override 986 public DirectoryStream<Path> newDirectoryStream(DirectoryStream.Filter<? super Path> filter) 987 throws IOException 988 { 989 checkRead(); 990 if (filter == null) 991 throw new NullPointerException(); 992 return new WindowsDirectoryStream(this, filter); 993 } 994 995 @Override 996 public void implCopyTo(Path obj, CopyOption... options) throws IOException { 997 WindowsPath target = (WindowsPath)obj; 998 WindowsFileCopy.copy(this, target, options); 999 } 1000 1001 @Override 1002 public void implMoveTo(Path obj, CopyOption... options) throws IOException { 1003 WindowsPath target = (WindowsPath)obj; 1004 WindowsFileCopy.move(this, target, options); 1005 } 1006 1007 private boolean followLinks(LinkOption... options) { 1008 boolean followLinks = true; 1009 for (LinkOption option: options) { 1010 if (option == LinkOption.NOFOLLOW_LINKS) { 1011 followLinks = false; 1012 continue; 1013 } 1014 if (option == null) 1015 throw new NullPointerException(); 1016 throw new AssertionError("Should not get here"); 1017 } 1018 return followLinks; 1019 } 1020 1021 @Override 1022 @SuppressWarnings("unchecked") 1023 public <V extends FileAttributeView> V 1024 getFileAttributeView(Class<V> view, LinkOption... options) 1025 { 1026 if (view == null) 1027 throw new NullPointerException(); 1028 boolean followLinks = followLinks(options); 1029 if (view == BasicFileAttributeView.class) 1030 return (V) WindowsFileAttributeViews.createBasicView(this, followLinks); 1031 if (view == DosFileAttributeView.class) 1032 return (V) WindowsFileAttributeViews.createDosView(this, followLinks); 1033 if (view == AclFileAttributeView.class) 1034 return (V) new WindowsAclFileAttributeView(this, followLinks); 1035 if (view == FileOwnerAttributeView.class) 1036 return (V) new FileOwnerAttributeViewImpl( 1037 new WindowsAclFileAttributeView(this, followLinks)); 1038 if (view == UserDefinedFileAttributeView.class) 1039 return (V) new WindowsUserDefinedFileAttributeView(this, followLinks); 1040 return (V) null; 1041 } 1042 1043 @Override 1044 public DynamicFileAttributeView getFileAttributeView(String name, LinkOption... options) { 1045 boolean followLinks = followLinks(options); 1046 if (name.equals("basic")) 1047 return WindowsFileAttributeViews.createBasicView(this, followLinks); 1048 if (name.equals("dos")) 1049 return WindowsFileAttributeViews.createDosView(this, followLinks); 1050 if (name.equals("acl")) 1051 return new WindowsAclFileAttributeView(this, followLinks); 1052 if (name.equals("owner")) 1053 return new FileOwnerAttributeViewImpl( 1054 new WindowsAclFileAttributeView(this, followLinks)); 1055 if (name.equals("user")) 1056 return new WindowsUserDefinedFileAttributeView(this, followLinks); 1057 return null; 1058 } 1059 1060 @Override 1061 public WindowsPath createDirectory(FileAttribute<?>... attrs) 1062 throws IOException 1063 { 1064 checkWrite(); 1065 WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs); 1066 try { 1067 CreateDirectory(getPathForWin32Calls(), sd.address()); 1068 } catch (WindowsException x) { 1069 x.rethrowAsIOException(this); 1070 } finally { 1071 sd.release(); 1072 } 1073 return this; 1074 } 1075 1076 @Override 1077 public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options, 1078 FileAttribute<?>... attrs) 1079 throws IOException 1080 { 1081 WindowsSecurityDescriptor sd = 1082 WindowsSecurityDescriptor.fromAttribute(attrs); 1083 try { 1084 return WindowsChannelFactory 1085 .newFileChannel(getPathForWin32Calls(), 1086 getPathForPermissionCheck(), 1087 options, 1088 sd.address()); 1089 } catch (WindowsException x) { 1090 x.rethrowAsIOException(this); 1091 return null; // keep compiler happy 1092 } finally { 1093 sd.release(); 1094 } 1095 } 1096 1097 @Override 1098 public boolean isSameFile(Path obj) throws IOException { 1099 if (this.equals(obj)) 1100 return true; 1101 if (!(obj instanceof WindowsPath)) // includes null check 1102 return false; 1103 WindowsPath other = (WindowsPath)obj; 1104 1105 // check security manager access to both files 1106 this.checkRead(); 1107 other.checkRead(); 1108 1109 // open both files and see if they are the same 1110 long h1 = 0L; 1111 try { 1112 h1 = this.openForReadAttributeAccess(true); 1113 } catch (WindowsException x) { 1114 x.rethrowAsIOException(this); 1115 } 1116 try { 1117 WindowsFileAttributes attrs1 = null; 1118 try { 1119 attrs1 = WindowsFileAttributes.readAttributes(h1); 1120 } catch (WindowsException x) { 1121 x.rethrowAsIOException(this); 1122 } 1123 long h2 = 0L; 1124 try { 1125 h2 = other.openForReadAttributeAccess(true); 1126 } catch (WindowsException x) { 1127 x.rethrowAsIOException(other); 1128 } 1129 try { 1130 WindowsFileAttributes attrs2 = null; 1131 try { 1132 attrs2 = WindowsFileAttributes.readAttributes(h2); 1133 } catch (WindowsException x) { 1134 x.rethrowAsIOException(other); 1135 } 1136 return WindowsFileAttributes.isSameFile(attrs1, attrs2); 1137 } finally { 1138 CloseHandle(h2); 1139 } 1140 } finally { 1141 CloseHandle(h1); 1142 } 1143 } 1144 1145 @Override 1146 public WindowsPath createSymbolicLink(Path obj, FileAttribute<?>... attrs) 1147 throws IOException 1148 { 1149 if (!getFileSystem().supportsLinks()) { 1150 throw new UnsupportedOperationException("Symbolic links not supported " 1151 + "on this operating system"); 1152 } 1153 1154 WindowsPath target = checkPath(obj); 1155 1156 // no attributes allowed 1157 if (attrs.length > 0) { 1158 WindowsSecurityDescriptor.fromAttribute(attrs); // may throw NPE or UOE 1159 throw new UnsupportedOperationException("Initial file attributes" + 1160 "not supported when creating symbolic link"); 1161 } 1162 1163 // permission check 1164 SecurityManager sm = System.getSecurityManager(); 1165 if (sm != null) { 1166 sm.checkPermission(new LinkPermission("symbolic")); 1167 this.checkWrite(); 1168 } 1169 1170 /** 1171 * Throw I/O exception for the drive-relative case because Windows 1172 * creates a link with the resolved target for this case. 1173 */ 1174 if (target.type == WindowsPathType.DRIVE_RELATIVE) { 1175 throw new IOException("Cannot create symbolic link to working directory relative target"); 1176 } 1177 1178 /* 1179 * Windows treates symbolic links to directories differently than it 1180 * does to other file types. For that reason we check if the exists and 1181 * is a directory. 1182 */ 1183 int flags = 0; 1184 WindowsPath resolvedTarget = 1185 WindowsPath.createFromNormalizedPath(getFileSystem(), resolve(target).path); 1186 try { 1187 if (WindowsFileAttributes.get(resolvedTarget, true).isDirectory()) 1188 flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; 1189 } catch (WindowsException x) { 1190 // unable to access target so assume target is not a directory 1191 } 1192 1193 // create the link 1194 try { 1195 CreateSymbolicLink(getPathForWin32Calls(), 1196 addPrefixIfNeeded(target.toString()), 1197 flags); 1198 } catch (WindowsException x) { 1199 if (x.lastError() == ERROR_INVALID_REPARSE_DATA) { 1200 x.rethrowAsIOException(this, target); 1201 } else { 1202 x.rethrowAsIOException(this); 1203 } 1204 } 1205 return this; 1206 } 1207 1208 @Override 1209 public Path createLink(Path obj) throws IOException { 1210 WindowsPath existing = checkPath(obj); 1211 1212 // permission check 1213 SecurityManager sm = System.getSecurityManager(); 1214 if (sm != null) { 1215 sm.checkPermission(new LinkPermission("hard")); 1216 this.checkWrite(); 1217 existing.checkWrite(); 1218 } 1219 1220 // create hard link 1221 try { 1222 CreateHardLink(this.getPathForWin32Calls(), 1223 existing.getPathForWin32Calls()); 1224 } catch (WindowsException x) { 1225 x.rethrowAsIOException(this, existing); 1226 } 1227 1228 return this; 1229 } 1230 1231 @Override 1232 public WindowsPath readSymbolicLink() throws IOException { 1233 if (!getFileSystem().supportsLinks()) { 1234 throw new UnsupportedOperationException("symbolic links not supported"); 1235 } 1236 1237 // permission check 1238 SecurityManager sm = System.getSecurityManager(); 1239 if (sm != null) { 1240 FilePermission perm = new FilePermission(getPathForPermissionCheck(), 1241 SecurityConstants.FILE_READLINK_ACTION); 1242 AccessController.checkPermission(perm); 1243 } 1244 1245 String target = WindowsLinkSupport.readLink(this); 1246 return createFromNormalizedPath(getFileSystem(), target); 1247 } 1248 1249 @Override 1250 public URI toUri() { 1251 return WindowsUriSupport.toUri(this); 1252 } 1253 1254 @Override 1255 public WindowsPath toAbsolutePath() { 1256 if (isAbsolute()) 1257 return this; 1258 1259 // permission check as per spec 1260 SecurityManager sm = System.getSecurityManager(); 1261 if (sm != null) { 1262 sm.checkPropertyAccess("user.dir"); 1263 } 1264 1265 try { 1266 return createFromNormalizedPath(getFileSystem(), getAbsolutePath()); 1267 } catch (WindowsException x) { 1268 throw new IOError(new IOException(x.getMessage())); 1269 } 1270 } 1271 1272 @Override 1273 public WindowsPath toRealPath(boolean resolveLinks) throws IOException { 1274 checkRead(); 1275 String rp = WindowsLinkSupport.getRealPath(this, resolveLinks); 1276 return createFromNormalizedPath(getFileSystem(), rp); 1277 } 1278 1279 @Override 1280 public boolean isHidden() throws IOException { 1281 checkRead(); 1282 WindowsFileAttributes attrs = null; 1283 try { 1284 attrs = WindowsFileAttributes.get(this, true); 1285 } catch (WindowsException x) { 1286 x.rethrowAsIOException(this); 1287 } 1288 // DOS hidden attribute not meaningful when set on directories 1289 if (attrs.isDirectory()) 1290 return false; 1291 return attrs.isHidden(); 1292 } 1293 1294 @Override 1295 public WatchKey register(WatchService watcher, 1296 WatchEvent.Kind<?>[] events, 1297 WatchEvent.Modifier... modifiers) 1298 throws IOException 1299 { 1300 if (watcher == null) 1301 throw new NullPointerException(); 1302 if (!(watcher instanceof WindowsWatchService)) 1303 throw new ProviderMismatchException(); 1304 1305 // When a security manager is set then we need to make a defensive 1306 // copy of the modifiers and check for the Windows specific FILE_TREE 1307 // modifier. When the modifier is present then check that permission 1308 // has been granted recursively. 1309 SecurityManager sm = System.getSecurityManager(); 1310 if (sm != null) { 1311 boolean watchSubtree = false; 1312 final int ml = modifiers.length; 1313 if (ml > 0) { 1314 modifiers = Arrays.copyOf(modifiers, ml); 1315 int i=0; 1316 while (i < ml) { 1317 if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) { 1318 watchSubtree = true; 1319 break; 1320 } 1321 } 1322 } 1323 String s = getPathForPermissionCheck(); 1324 sm.checkRead(s); 1325 if (watchSubtree) 1326 sm.checkRead(s + "\\-"); 1327 } 1328 1329 return ((WindowsWatchService)watcher).register(this, events, modifiers); 1330 } 1331 }