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