1 /* 2 * Copyright (c) 2008, 2009, 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.file.*; 29 import java.nio.file.attribute.*; 30 import java.io.*; 31 import java.net.URI; 32 import java.util.*; 33 import java.lang.ref.WeakReference; 34 35 import com.sun.nio.file.ExtendedWatchEventModifier; 36 37 import static sun.nio.fs.WindowsNativeDispatcher.*; 38 import static sun.nio.fs.WindowsConstants.*; 39 40 /** 41 * Windows implementation of Path 42 */ 43 44 class WindowsPath extends AbstractPath { 45 46 // The maximum path that does not require long path prefix. On Windows 47 // the maximum path is 260 minus 1 (NUL) but for directories it is 260 48 // minus 12 minus 1 (to allow for the creation of a 8.3 file in the 49 // directory). 50 private static final int MAX_PATH = 247; 51 52 // Maximum extended-length path 53 private static final int MAX_LONG_PATH = 32000; 54 55 // FIXME - eliminate this reference to reduce space 56 private final WindowsFileSystem fs; 57 58 // path type 59 private final WindowsPathType type; 60 // root component (may be empty) 61 private final String root; 62 // normalized path 63 private final String path; 64 65 // the path to use in Win32 calls. This differs from path for relative 66 // paths and has a long path prefix for all paths longer than MAX_PATH. 67 private volatile WeakReference<String> pathForWin32Calls; 68 69 // offsets into name components (computed lazily) 70 private volatile Integer[] offsets; 71 72 // computed hash code (computed lazily, no need to be volatile) 73 private int hash; 74 75 76 /** 77 * Initializes a new instance of this class. 78 */ 79 private WindowsPath(WindowsFileSystem fs, 80 WindowsPathType type, 81 String root, 82 String path) 83 { 84 this.fs = fs; 85 this.type = type; 86 this.root = root; 87 this.path = path; 88 } 89 90 /** 91 * Creates a Path by parsing the given path. 92 */ 93 static WindowsPath parse(WindowsFileSystem fs, String path) { 94 WindowsPathParser.Result result = WindowsPathParser.parse(path); 95 return new WindowsPath(fs, result.type(), result.root(), result.path()); 96 } 97 98 /** 99 * Creates a Path from a given path that is known to be normalized. 100 */ 101 static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, 102 String path, 103 BasicFileAttributes attrs) 104 { 105 try { 106 WindowsPathParser.Result result = 107 WindowsPathParser.parseNormalizedPath(path); 108 if (attrs == null) { 109 return new WindowsPath(fs, 110 result.type(), 111 result.root(), 112 result.path()); 113 } else { 114 return new WindowsPathWithAttributes(fs, 115 result.type(), 116 result.root(), 117 result.path(), 118 attrs); 119 } 120 } catch (InvalidPathException x) { 121 throw new AssertionError(x.getMessage()); 122 } 123 } 124 125 /** 126 * Creates a WindowsPath from a given path that is known to be normalized. 127 */ 128 static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, 129 String path) 130 { 131 return createFromNormalizedPath(fs, path, null); 132 } 133 134 /** 135 * Special implementation with attached/cached attributes (used to quicken 136 * file tree traveral) 137 */ 138 private static class WindowsPathWithAttributes 139 extends WindowsPath implements BasicFileAttributesHolder 140 { 141 final WeakReference<BasicFileAttributes> ref; 142 143 WindowsPathWithAttributes(WindowsFileSystem fs, 144 WindowsPathType type, 145 String root, 146 String path, 147 BasicFileAttributes attrs) 148 { 149 super(fs, type, root, path); 150 ref = new WeakReference<BasicFileAttributes>(attrs); 151 } 152 153 @Override 154 public BasicFileAttributes get() { 155 return ref.get(); 156 } 157 158 @Override 159 public void invalidate() { 160 ref.clear(); 161 } 162 163 // no need to override equals/hashCode. 164 } 165 166 // use this message when throwing exceptions 167 String getPathForExceptionMessage() { 168 return path; 169 } 170 171 // use this path for permission checks 172 String getPathForPermissionCheck() { 173 return path; 174 } 175 176 // use this path for Win32 calls 177 // This method will prefix long paths with \\?\ or \\?\UNC as required. 178 String getPathForWin32Calls() throws WindowsException { 179 // short absolute paths can be used directly 180 if (isAbsolute() && path.length() <= MAX_PATH) 181 return path; 182 183 // return cached values if available 184 WeakReference<String> ref = pathForWin32Calls; 185 String resolved = (ref != null) ? ref.get() : null; 186 if (resolved != null) { 187 // Win32 path already available 188 return resolved; 189 } 190 191 // resolve against default directory 192 resolved = getAbsolutePath(); 193 194 // Long paths need to have "." and ".." removed and be prefixed with 195 // "\\?\". Note that it is okay to remove ".." even when it follows 196 // a link - for example, it is okay for foo/link/../bar to be changed 197 // to foo/bar. The reason is that Win32 APIs to access foo/link/../bar 198 // will access foo/bar anyway (which differs to Unix systems) 199 if (resolved.length() > MAX_PATH) { 200 if (resolved.length() > MAX_LONG_PATH) { 201 throw new WindowsException("Cannot access file with path exceeding " 202 + MAX_LONG_PATH + " characters"); 203 } 204 resolved = addPrefixIfNeeded(GetFullPathName(resolved)); 205 } 206 207 // cache the resolved path (except drive relative paths as the working 208 // directory on removal media devices can change during the lifetime 209 // of the VM) 210 if (type != WindowsPathType.DRIVE_RELATIVE) { 211 synchronized (path) { 212 pathForWin32Calls = new WeakReference<String>(resolved); 213 } 214 } 215 return resolved; 216 } 217 218 // return this path resolved against the file system's default directory 219 private String getAbsolutePath() throws WindowsException { 220 if (isAbsolute()) 221 return path; 222 223 // Relative path ("foo" for example) 224 if (type == WindowsPathType.RELATIVE) { 225 String defaultDirectory = getFileSystem().defaultDirectory(); 226 if (isEmpty()) 227 return defaultDirectory; 228 if (defaultDirectory.endsWith("\\")) { 229 return defaultDirectory + path; 230 } else { 231 StringBuilder sb = 232 new StringBuilder(defaultDirectory.length() + path.length() + 1); 233 return sb.append(defaultDirectory).append('\\').append(path).toString(); 234 } 235 } 236 237 // Directory relative path ("\foo" for example) 238 if (type == WindowsPathType.DIRECTORY_RELATIVE) { 239 String defaultRoot = getFileSystem().defaultRoot(); 240 return defaultRoot + path.substring(1); 241 } 242 243 // Drive relative path ("C:foo" for example). 244 if (isSameDrive(root, getFileSystem().defaultRoot())) { 245 // relative to default directory 246 String remaining = path.substring(root.length()); 247 String defaultDirectory = getFileSystem().defaultDirectory(); 248 String result; 249 if (defaultDirectory.endsWith("\\")) { 250 result = defaultDirectory + remaining; 251 } else { 252 result = defaultDirectory + "\\" + remaining; 253 } 254 return result; 255 } else { 256 // relative to some other drive 257 String wd; 258 try { 259 int dt = GetDriveType(root + "\\"); 260 if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR) 261 throw new WindowsException(""); 262 wd = GetFullPathName(root + "."); 263 } catch (WindowsException x) { 264 throw new WindowsException("Unable to get working directory of drive '" + 265 Character.toUpperCase(root.charAt(0)) + "'"); 266 } 267 String result = wd; 268 if (wd.endsWith("\\")) { 269 result += path.substring(root.length()); 270 } else { 271 if (path.length() > root.length()) 272 result += "\\" + path.substring(root.length()); 273 } 274 return result; 275 } 276 } 277 278 // returns true if same drive letter 279 private static boolean isSameDrive(String root1, String root2) { 280 return Character.toUpperCase(root1.charAt(0)) == 281 Character.toUpperCase(root2.charAt(0)); 282 } 283 284 // Add long path prefix to path if required 285 static String addPrefixIfNeeded(String path) { 286 if (path.length() > 248) { 287 if (path.startsWith("\\\\")) { 288 path = "\\\\?\\UNC" + path.substring(1, path.length()); 289 } else { 290 path = "\\\\?\\" + path; 291 } 292 } 293 return path; 294 } 295 296 @Override 297 public WindowsFileSystem getFileSystem() { 298 return fs; 299 } 300 301 // -- Path operations -- 302 303 private boolean isEmpty() { 304 return path.length() == 0; 305 } 306 307 private WindowsPath emptyPath() { 308 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", ""); 309 } 310 311 @Override 312 public Path getFileName() { 313 int len = path.length(); 314 // represents empty path 315 if (len == 0) 316 return this; 317 // represents root component only 318 if (root.length() == len) 319 return null; 320 int off = path.lastIndexOf('\\'); 321 if (off < root.length()) 322 off = root.length(); 323 else 324 off++; 325 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off)); 326 } 327 328 @Override 329 public WindowsPath getParent() { 330 // represents root component only 331 if (root.length() == path.length()) 332 return null; 333 int off = path.lastIndexOf('\\'); 334 if (off < root.length()) 335 return getRoot(); 336 else 337 return new WindowsPath(getFileSystem(), 338 type, 339 root, 340 path.substring(0, off)); 341 } 342 343 @Override 344 public WindowsPath getRoot() { 345 if (root.length() == 0) 346 return null; 347 return new WindowsPath(getFileSystem(), type, root, root); 348 } 349 350 // package-private 351 WindowsPathType type() { 352 return type; 353 } 354 355 // package-private 356 boolean isUnc() { 357 return type == WindowsPathType.UNC; 358 } 359 360 boolean needsSlashWhenResolving() { 361 if (path.endsWith("\\")) 362 return false; 363 return path.length() > root.length(); 364 } 365 366 @Override 367 public boolean isAbsolute() { 368 return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC; 369 } 370 371 static WindowsPath toWindowsPath(Path path) { 372 if (path == null) 373 throw new NullPointerException(); 374 if (!(path instanceof WindowsPath)) { 375 throw new ProviderMismatchException(); 376 } 377 return (WindowsPath)path; 378 } 379 380 @Override 381 public WindowsPath relativize(Path obj) { 382 WindowsPath other = toWindowsPath(obj); 383 if (this.equals(other)) 384 return emptyPath(); 385 386 // can only relativize paths of the same type 387 if (this.type != other.type) 388 throw new IllegalArgumentException("'other' is different type of Path"); 389 390 // can only relativize paths if root component matches 391 if (!this.root.equalsIgnoreCase(other.root)) 392 throw new IllegalArgumentException("'other' has different root"); 393 394 int bn = this.getNameCount(); 395 int cn = other.getNameCount(); 396 397 // skip matching names 398 int n = (bn > cn) ? cn : bn; 399 int i = 0; 400 while (i < n) { 401 if (!this.getName(i).equals(other.getName(i))) 402 break; 403 i++; 404 } 405 406 // append ..\ for remaining names in the base 407 StringBuilder result = new StringBuilder(); 408 for (int j=i; j<bn; j++) { 409 result.append("..\\"); 410 } 411 412 // append remaining names in child 413 for (int j=i; j<cn; j++) { 414 result.append(other.getName(j).toString()); 415 result.append("\\"); 416 } 417 418 // drop trailing slash in result 419 result.setLength(result.length()-1); 420 return createFromNormalizedPath(getFileSystem(), result.toString()); 421 } 422 423 @Override 424 public Path normalize() { 425 final int count = getNameCount(); 426 if (count == 0 || isEmpty()) 427 return this; 428 429 boolean[] ignore = new boolean[count]; // true => ignore name 430 int remaining = count; // number of names remaining 431 432 // multiple passes to eliminate all occurences of "." and "name/.." 433 int prevRemaining; 434 do { 435 prevRemaining = remaining; 436 int prevName = -1; 437 for (int i=0; i<count; i++) { 438 if (ignore[i]) 439 continue; 440 441 String name = elementAsString(i); 442 443 // not "." or ".." 444 if (name.length() > 2) { 445 prevName = i; 446 continue; 447 } 448 449 // "." or something else 450 if (name.length() == 1) { 451 // ignore "." 452 if (name.charAt(0) == '.') { 453 ignore[i] = true; 454 remaining--; 455 } else { 456 prevName = i; 457 } 458 continue; 459 } 460 461 // not ".." 462 if (name.charAt(0) != '.' || name.charAt(1) != '.') { 463 prevName = i; 464 continue; 465 } 466 467 // ".." found 468 if (prevName >= 0) { 469 // name/<ignored>/.. found so mark name and ".." to be 470 // ignored 471 ignore[prevName] = true; 472 ignore[i] = true; 473 remaining = remaining - 2; 474 prevName = -1; 475 } else { 476 // Cases: 477 // C:\<ignored>\.. 478 // \\server\\share\<ignored>\.. 479 // \<ignored>.. 480 if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) { 481 boolean hasPrevious = false; 482 for (int j=0; j<i; j++) { 483 if (!ignore[j]) { 484 hasPrevious = true; 485 break; 486 } 487 } 488 if (!hasPrevious) { 489 // all proceeding names are ignored 490 ignore[i] = true; 491 remaining--; 492 } 493 } 494 } 495 } 496 } while (prevRemaining > remaining); 497 498 // no redundant names 499 if (remaining == count) 500 return this; 501 502 // corner case - all names removed 503 if (remaining == 0) { 504 return (root.length() == 0) ? emptyPath() : getRoot(); 505 } 506 507 // re-constitute the path from the remaining names. 508 StringBuilder result = new StringBuilder(); 509 if (root != null) 510 result.append(root); 511 for (int i=0; i<count; i++) { 512 if (!ignore[i]) { 513 result.append(getName(i)); 514 result.append("\\"); 515 } 516 } 517 518 // drop trailing slash in result 519 result.setLength(result.length()-1); 520 return createFromNormalizedPath(getFileSystem(), result.toString()); 521 } 522 523 @Override 524 public WindowsPath resolve(Path obj) { 525 WindowsPath other = toWindowsPath(obj); 526 if (other.isEmpty()) 527 return this; 528 if (other.isAbsolute()) 529 return other; 530 531 switch (other.type) { 532 case RELATIVE: { 533 String result; 534 if (path.endsWith("\\") || (root.length() == path.length())) { 535 result = path + other.path; 536 } else { 537 result = path + "\\" + other.path; 538 } 539 return new WindowsPath(getFileSystem(), type, root, result); 540 } 541 542 case DIRECTORY_RELATIVE: { 543 String result; 544 if (root.endsWith("\\")) { 545 result = root + other.path.substring(1); 546 } else { 547 result = root + other.path; 548 } 549 return createFromNormalizedPath(getFileSystem(), result); 550 } 551 552 case DRIVE_RELATIVE: { 553 if (!root.endsWith("\\")) 554 return other; 555 // if different roots then return other 556 String thisRoot = root.substring(0, root.length()-1); 557 if (!thisRoot.equalsIgnoreCase(other.root)) 558 return other; 559 // same roots 560 String remaining = other.path.substring(other.root.length()); 561 String result; 562 if (path.endsWith("\\")) { 563 result = path + remaining; 564 } else { 565 result = path + "\\" + remaining; 566 } 567 return createFromNormalizedPath(getFileSystem(), result); 568 } 569 570 default: 571 throw new AssertionError(); 572 } 573 } 574 575 // generate offset array 576 private void initOffsets() { 577 if (offsets == null) { 578 ArrayList<Integer> list = new ArrayList<>(); 579 if (isEmpty()) { 580 // empty path considered to have one name element 581 list.add(0); 582 } else { 583 int start = root.length(); 584 int off = root.length(); 585 while (off < path.length()) { 586 if (path.charAt(off) != '\\') { 587 off++; 588 } else { 589 list.add(start); 590 start = ++off; 591 } 592 } 593 if (start != off) 594 list.add(start); 595 } 596 synchronized (this) { 597 if (offsets == null) 598 offsets = list.toArray(new Integer[list.size()]); 599 } 600 } 601 } 602 603 @Override 604 public int getNameCount() { 605 initOffsets(); 606 return offsets.length; 607 } 608 609 private String elementAsString(int i) { 610 initOffsets(); 611 if (i == (offsets.length-1)) 612 return path.substring(offsets[i]); 613 return path.substring(offsets[i], offsets[i+1]-1); 614 } 615 616 @Override 617 public WindowsPath getName(int index) { 618 initOffsets(); 619 if (index < 0 || index >= offsets.length) 620 throw new IllegalArgumentException(); 621 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index)); 622 } 623 624 @Override 625 public WindowsPath subpath(int beginIndex, int endIndex) { 626 initOffsets(); 627 if (beginIndex < 0) 628 throw new IllegalArgumentException(); 629 if (beginIndex >= offsets.length) 630 throw new IllegalArgumentException(); 631 if (endIndex > offsets.length) 632 throw new IllegalArgumentException(); 633 if (beginIndex >= endIndex) 634 throw new IllegalArgumentException(); 635 636 StringBuilder sb = new StringBuilder(); 637 Integer[] nelems = new Integer[endIndex - beginIndex]; 638 for (int i = beginIndex; i < endIndex; i++) { 639 nelems[i-beginIndex] = sb.length(); 640 sb.append(elementAsString(i)); 641 if (i != (endIndex-1)) 642 sb.append("\\"); 643 } 644 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString()); 645 } 646 647 @Override 648 public boolean startsWith(Path obj) { 649 if (!(Objects.requireNonNull(obj) instanceof WindowsPath)) 650 return false; 651 WindowsPath other = (WindowsPath)obj; 652 653 // if this path has a root component the given path's root must match 654 if (!this.root.equalsIgnoreCase(other.root)) { 655 return false; 656 } 657 658 // empty path starts with itself 659 if (other.isEmpty()) 660 return this.isEmpty(); 661 662 // roots match so compare elements 663 int thisCount = getNameCount(); 664 int otherCount = other.getNameCount(); 665 if (otherCount <= thisCount) { 666 while (--otherCount >= 0) { 667 String thisElement = this.elementAsString(otherCount); 668 String otherElement = other.elementAsString(otherCount); 669 // FIXME: should compare in uppercase 670 if (!thisElement.equalsIgnoreCase(otherElement)) 671 return false; 672 } 673 return true; 674 } 675 return false; 676 } 677 678 @Override 679 public boolean endsWith(Path obj) { 680 if (!(Objects.requireNonNull(obj) instanceof WindowsPath)) 681 return false; 682 WindowsPath other = (WindowsPath)obj; 683 684 // other path is longer 685 if (other.path.length() > this.path.length()) { 686 return false; 687 } 688 689 // empty path ends in itself 690 if (other.isEmpty()) { 691 return this.isEmpty(); 692 } 693 694 int thisCount = this.getNameCount(); 695 int otherCount = other.getNameCount(); 696 697 // given path has more elements that this path 698 if (otherCount > thisCount) { 699 return false; 700 } 701 702 // compare roots 703 if (other.root.length() > 0) { 704 if (otherCount < thisCount) 705 return false; 706 // FIXME: should compare in uppercase 707 if (!this.root.equalsIgnoreCase(other.root)) 708 return false; 709 } 710 711 // match last 'otherCount' elements 712 int off = thisCount - otherCount; 713 while (--otherCount >= 0) { 714 String thisElement = this.elementAsString(off + otherCount); 715 String otherElement = other.elementAsString(otherCount); 716 // FIXME: should compare in uppercase 717 if (!thisElement.equalsIgnoreCase(otherElement)) 718 return false; 719 } 720 return true; 721 } 722 723 @Override 724 public int compareTo(Path obj) { 725 if (obj == null) 726 throw new NullPointerException(); 727 String s1 = path; 728 String s2 = ((WindowsPath)obj).path; 729 int n1 = s1.length(); 730 int n2 = s2.length(); 731 int min = Math.min(n1, n2); 732 for (int i = 0; i < min; i++) { 733 char c1 = s1.charAt(i); 734 char c2 = s2.charAt(i); 735 if (c1 != c2) { 736 c1 = Character.toUpperCase(c1); 737 c2 = Character.toUpperCase(c2); 738 if (c1 != c2) { 739 return c1 - c2; 740 } 741 } 742 } 743 return n1 - n2; 744 } 745 746 @Override 747 public boolean equals(Object obj) { 748 if ((obj != null) && (obj instanceof WindowsPath)) { 749 return compareTo((Path)obj) == 0; 750 } 751 return false; 752 } 753 754 @Override 755 public int hashCode() { 756 // OK if two or more threads compute hash 757 int h = hash; 758 if (h == 0) { 759 for (int i = 0; i< path.length(); i++) { 760 h = 31*h + Character.toUpperCase(path.charAt(i)); 761 } 762 hash = h; 763 } 764 return h; 765 } 766 767 @Override 768 public String toString() { 769 return path; 770 } 771 772 // -- file operations -- 773 774 // package-private 775 long openForReadAttributeAccess(boolean followLinks) 776 throws WindowsException 777 { 778 int flags = FILE_FLAG_BACKUP_SEMANTICS; 779 if (!followLinks && getFileSystem().supportsLinks()) 780 flags |= FILE_FLAG_OPEN_REPARSE_POINT; 781 return CreateFile(getPathForWin32Calls(), 782 FILE_READ_ATTRIBUTES, 783 (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 784 0L, 785 OPEN_EXISTING, 786 flags); 787 } 788 789 void checkRead() { 790 SecurityManager sm = System.getSecurityManager(); 791 if (sm != null) { 792 sm.checkRead(getPathForPermissionCheck()); 793 } 794 } 795 796 void checkWrite() { 797 SecurityManager sm = System.getSecurityManager(); 798 if (sm != null) { 799 sm.checkWrite(getPathForPermissionCheck()); 800 } 801 } 802 803 void checkDelete() { 804 SecurityManager sm = System.getSecurityManager(); 805 if (sm != null) { 806 sm.checkDelete(getPathForPermissionCheck()); 807 } 808 } 809 810 @Override 811 public URI toUri() { 812 return WindowsUriSupport.toUri(this); 813 } 814 815 @Override 816 public WindowsPath toAbsolutePath() { 817 if (isAbsolute()) 818 return this; 819 820 // permission check as per spec 821 SecurityManager sm = System.getSecurityManager(); 822 if (sm != null) { 823 sm.checkPropertyAccess("user.dir"); 824 } 825 826 try { 827 return createFromNormalizedPath(getFileSystem(), getAbsolutePath()); 828 } catch (WindowsException x) { 829 throw new IOError(new IOException(x.getMessage())); 830 } 831 } 832 833 @Override 834 public WindowsPath toRealPath(LinkOption... options) throws IOException { 835 checkRead(); 836 String rp = WindowsLinkSupport.getRealPath(this, Util.followLinks(options)); 837 return createFromNormalizedPath(getFileSystem(), rp); 838 } 839 840 @Override 841 public WatchKey register(WatchService watcher, 842 WatchEvent.Kind<?>[] events, 843 WatchEvent.Modifier... modifiers) 844 throws IOException 845 { 846 if (watcher == null) 847 throw new NullPointerException(); 848 if (!(watcher instanceof WindowsWatchService)) 849 throw new ProviderMismatchException(); 850 851 // When a security manager is set then we need to make a defensive 852 // copy of the modifiers and check for the Windows specific FILE_TREE 853 // modifier. When the modifier is present then check that permission 854 // has been granted recursively. 855 SecurityManager sm = System.getSecurityManager(); 856 if (sm != null) { 857 boolean watchSubtree = false; 858 final int ml = modifiers.length; 859 if (ml > 0) { 860 modifiers = Arrays.copyOf(modifiers, ml); 861 int i=0; 862 while (i < ml) { 863 if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) { 864 watchSubtree = true; 865 break; 866 } 867 } 868 } 869 String s = getPathForPermissionCheck(); 870 sm.checkRead(s); 871 if (watchSubtree) 872 sm.checkRead(s + "\\-"); 873 } 874 875 return ((WindowsWatchService)watcher).register(this, events, modifiers); 876 } 877 }