src/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java

Print this page




   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.spi.*;
  30 import java.nio.file.attribute.*;
  31 import java.nio.channels.*;
  32 import java.net.URI;
  33 import java.util.concurrent.ExecutorService;
  34 import java.io.IOException;
  35 import java.util.*;
  36 

  37 import sun.nio.ch.ThreadPool;

  38 



  39 public class WindowsFileSystemProvider
  40     extends FileSystemProvider
  41 {


  42     private static final String USER_DIR = "user.dir";
  43     private final WindowsFileSystem theFileSystem;
  44 
  45     public WindowsFileSystemProvider() {
  46         theFileSystem = new WindowsFileSystem(this, System.getProperty(USER_DIR));
  47     }
  48 
  49     @Override
  50     public String getScheme() {
  51         return "file";
  52     }
  53 
  54     private void checkUri(URI uri) {
  55         if (!uri.getScheme().equalsIgnoreCase(getScheme()))
  56             throw new IllegalArgumentException("URI does not match this provider");
  57         if (uri.getAuthority() != null)
  58             throw new IllegalArgumentException("Authority component present");
  59         if (uri.getPath() == null)
  60             throw new IllegalArgumentException("Path component is undefined");
  61         if (!uri.getPath().equals("/"))


 126             throw new ProviderMismatchException();
 127         WindowsPath file = (WindowsPath)path;
 128         ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
 129         WindowsSecurityDescriptor sd =
 130             WindowsSecurityDescriptor.fromAttribute(attrs);
 131         try {
 132             return WindowsChannelFactory
 133                 .newAsynchronousFileChannel(file.getPathForWin32Calls(),
 134                                             file.getPathForPermissionCheck(),
 135                                             options,
 136                                             sd.address(),
 137                                             pool);
 138         } catch (WindowsException x) {
 139             x.rethrowAsIOException(file);
 140             return null;
 141         } finally {
 142             if (sd != null)
 143                 sd.release();
 144         }
 145     }





















































































































































































































































































































































































































































































































 146 }


   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.nio.channels.*;
  31 import java.net.URI;
  32 import java.util.concurrent.ExecutorService;
  33 import java.io.*;
  34 import java.util.*;
  35 import java.security.AccessController;
  36 import sun.misc.Unsafe;
  37 import sun.nio.ch.ThreadPool;
  38 import sun.security.util.SecurityConstants;
  39 
  40 import static sun.nio.fs.WindowsNativeDispatcher.*;
  41 import static sun.nio.fs.WindowsConstants.*;
  42 
  43 public class WindowsFileSystemProvider
  44     extends AbstractFileSystemProvider
  45 {
  46     private static final Unsafe unsafe = Unsafe.getUnsafe();
  47     
  48     private static final String USER_DIR = "user.dir";
  49     private final WindowsFileSystem theFileSystem;
  50 
  51     public WindowsFileSystemProvider() {
  52         theFileSystem = new WindowsFileSystem(this, System.getProperty(USER_DIR));
  53     }
  54 
  55     @Override
  56     public String getScheme() {
  57         return "file";
  58     }
  59 
  60     private void checkUri(URI uri) {
  61         if (!uri.getScheme().equalsIgnoreCase(getScheme()))
  62             throw new IllegalArgumentException("URI does not match this provider");
  63         if (uri.getAuthority() != null)
  64             throw new IllegalArgumentException("Authority component present");
  65         if (uri.getPath() == null)
  66             throw new IllegalArgumentException("Path component is undefined");
  67         if (!uri.getPath().equals("/"))


 132             throw new ProviderMismatchException();
 133         WindowsPath file = (WindowsPath)path;
 134         ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
 135         WindowsSecurityDescriptor sd =
 136             WindowsSecurityDescriptor.fromAttribute(attrs);
 137         try {
 138             return WindowsChannelFactory
 139                 .newAsynchronousFileChannel(file.getPathForWin32Calls(),
 140                                             file.getPathForPermissionCheck(),
 141                                             options,
 142                                             sd.address(),
 143                                             pool);
 144         } catch (WindowsException x) {
 145             x.rethrowAsIOException(file);
 146             return null;
 147         } finally {
 148             if (sd != null)
 149                 sd.release();
 150         }
 151     }
 152    
 153     private boolean followLinks(LinkOption... options) {
 154         boolean followLinks = true;
 155         for (LinkOption option: options) {
 156             if (option == LinkOption.NOFOLLOW_LINKS) {
 157                 followLinks = false;
 158                 continue;
 159             }
 160             if (option == null)
 161                 throw new NullPointerException();
 162             throw new AssertionError("Should not get here");
 163         }
 164         return followLinks;
 165     }    
 166 
 167     @Override
 168     @SuppressWarnings("unchecked")
 169     public <V extends FileAttributeView> V
 170         getFileAttributeView(Path obj, Class<V> view, LinkOption... options)
 171     {
 172         WindowsPath file = WindowsPath.toWindowsPath(obj);
 173         if (view == null)
 174             throw new NullPointerException();
 175         boolean followLinks = followLinks(options);
 176         if (view == BasicFileAttributeView.class)
 177             return (V) WindowsFileAttributeViews.createBasicView(file, followLinks);
 178         if (view == DosFileAttributeView.class)
 179             return (V) WindowsFileAttributeViews.createDosView(file, followLinks);
 180         if (view == AclFileAttributeView.class)
 181             return (V) new WindowsAclFileAttributeView(file, followLinks);
 182         if (view == FileOwnerAttributeView.class)
 183             return (V) new FileOwnerAttributeViewImpl(
 184                 new WindowsAclFileAttributeView(file, followLinks));
 185         if (view == UserDefinedFileAttributeView.class)
 186             return (V) new WindowsUserDefinedFileAttributeView(file, followLinks);
 187         return (V) null;
 188     }
 189 
 190     @Override
 191     @SuppressWarnings("unchecked")
 192     public <A extends BasicFileAttributes> A readAttributes(Path file,
 193                                                             Class<A> type,
 194                                                             LinkOption... options)
 195         throws IOException
 196     {
 197         Class<? extends BasicFileAttributeView> view;
 198         if (type == BasicFileAttributes.class)
 199             view = BasicFileAttributeView.class;
 200         else if (type == DosFileAttributes.class)
 201             view = DosFileAttributeView.class;
 202         else if (type == null)
 203             throw new NullPointerException();
 204         else
 205             throw new UnsupportedOperationException();
 206         return (A) getFileAttributeView(file, view, options).readAttributes();
 207     }
 208 
 209     @Override
 210     public DynamicFileAttributeView getFileAttributeView(Path obj, String name, LinkOption... options) {
 211         WindowsPath file = WindowsPath.toWindowsPath(obj);
 212         boolean followLinks = followLinks(options);
 213         if (name.equals("basic"))
 214             return WindowsFileAttributeViews.createBasicView(file, followLinks);
 215         if (name.equals("dos"))
 216             return WindowsFileAttributeViews.createDosView(file, followLinks);
 217         if (name.equals("acl"))
 218             return new WindowsAclFileAttributeView(file, followLinks);
 219         if (name.equals("owner"))
 220             return new FileOwnerAttributeViewImpl(
 221                 new WindowsAclFileAttributeView(file, followLinks));
 222         if (name.equals("user"))
 223             return new WindowsUserDefinedFileAttributeView(file, followLinks);
 224         return null;
 225     }
 226 
 227     @Override
 228     public SeekableByteChannel newByteChannel(Path obj,
 229                                               Set<? extends OpenOption> options,
 230                                               FileAttribute<?>... attrs)
 231          throws IOException
 232     {
 233         WindowsPath file = WindowsPath.toWindowsPath(obj);
 234         WindowsSecurityDescriptor sd =
 235             WindowsSecurityDescriptor.fromAttribute(attrs);
 236         try {
 237             return WindowsChannelFactory
 238                 .newFileChannel(file.getPathForWin32Calls(),
 239                                 file.getPathForPermissionCheck(),
 240                                 options,
 241                                 sd.address());
 242         } catch (WindowsException x) {
 243             x.rethrowAsIOException(file);
 244             return null;  // keep compiler happy
 245         } finally {
 246             sd.release();
 247         }
 248     }
 249 
 250     @Override
 251     boolean implDelete(Path obj, boolean failIfNotExists) throws IOException {
 252         WindowsPath file = WindowsPath.toWindowsPath(obj);
 253         file.checkDelete();
 254 
 255         WindowsFileAttributes attrs = null;
 256         try {
 257              // need to know if file is a directory or junction
 258              attrs = WindowsFileAttributes.get(file, false);
 259              if (attrs.isDirectory() || attrs.isDirectoryLink()) {
 260                 RemoveDirectory(file.getPathForWin32Calls());
 261              } else {
 262                 DeleteFile(file.getPathForWin32Calls());
 263              }
 264              return true;
 265         } catch (WindowsException x) {
 266 
 267             // no-op if file does not exist
 268             if (!failIfNotExists &&
 269                 (x.lastError() == ERROR_FILE_NOT_FOUND ||
 270                  x.lastError() == ERROR_PATH_NOT_FOUND)) return false;
 271 
 272             if (attrs != null && attrs.isDirectory()) {
 273                 // ERROR_ALREADY_EXISTS is returned when attempting to delete
 274                 // non-empty directory on SAMBA servers.
 275                 if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
 276                     x.lastError() == ERROR_ALREADY_EXISTS)
 277                 {
 278                     throw new DirectoryNotEmptyException(
 279                         file.getPathForExceptionMessage());
 280                 }
 281             }
 282             x.rethrowAsIOException(file);
 283             return false;
 284         }
 285     }
 286 
 287     @Override
 288     public void copy(Path source, Path target, CopyOption... options)
 289         throws IOException
 290     {
 291         WindowsFileCopy.copy(WindowsPath.toWindowsPath(source),
 292                              WindowsPath.toWindowsPath(target),
 293                              options);
 294     }
 295 
 296     @Override
 297     public void move(Path source, Path target, CopyOption... options)
 298         throws IOException
 299     {
 300         WindowsFileCopy.move(WindowsPath.toWindowsPath(source),
 301                              WindowsPath.toWindowsPath(target),
 302                              options);
 303     }
 304 
 305     /**
 306      * Returns buffer with SID_AND_ATTRIBUTES structure representing the user
 307      * associated with the current thread access token.
 308      * FIXME - this should be cached.
 309      */
 310     private static NativeBuffer getUserInfo(WindowsPath file) throws IOException {
 311         try {
 312             long hToken = WindowsSecurity.processTokenWithQueryAccess;
 313             int size = GetTokenInformation(hToken, TokenUser, 0L, 0);
 314             assert size > 0;
 315 
 316             NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
 317             try {
 318                 int newsize = GetTokenInformation(hToken, TokenUser,
 319                                                   buffer.address(), size);
 320                 if (newsize != size)
 321                     throw new AssertionError();
 322                 return buffer;
 323             } catch (WindowsException x) {
 324                 buffer.release();
 325                 throw x;
 326             }
 327         } catch (WindowsException x) {
 328             throw new IOException(x.getMessage());
 329         }
 330     }
 331     
 332     /**
 333      * Reads the file ACL and return the effective access as ACCESS_MASK
 334      */
 335     private static int getEffectiveAccess(WindowsPath file) throws IOException {
 336         // read security descriptor continaing ACL (symlinks are followed)
 337         String target = WindowsLinkSupport.getFinalPath(file, true);
 338         NativeBuffer aclBuffer = WindowsAclFileAttributeView
 339             .getFileSecurity(target, DACL_SECURITY_INFORMATION);
 340 
 341         // retrieves DACL from security descriptor
 342         long pAcl = GetSecurityDescriptorDacl(aclBuffer.address());
 343 
 344         // Use GetEffectiveRightsFromAcl to get effective access to file
 345         try {
 346             NativeBuffer userBuffer = getUserInfo(file);
 347             try {
 348                 try {
 349                     // SID_AND_ATTRIBUTES->pSid
 350                     long pSid = unsafe.getAddress(userBuffer.address());
 351                     long pTrustee = BuildTrusteeWithSid(pSid);
 352                     try {
 353                         return GetEffectiveRightsFromAcl(pAcl, pTrustee);
 354                     } finally {
 355                         LocalFree(pTrustee);
 356                     }
 357                 } catch (WindowsException x) {
 358                     throw new IOException("Unable to get effective rights from ACL: " +
 359                         x.getMessage());
 360                 }
 361             } finally {
 362                 userBuffer.release();
 363             }
 364         } finally {
 365             aclBuffer.release();
 366         }
 367     }
 368 
 369     @Override
 370     public void checkAccess(Path obj, AccessMode... modes) throws IOException {
 371         WindowsPath file = WindowsPath.toWindowsPath(obj);
 372         // if no access modes then simply file attributes
 373         if (modes.length == 0) {
 374             file.checkRead();
 375             try {
 376                 WindowsFileAttributes.get(file, true);
 377             } catch (WindowsException exc) {
 378                 exc.rethrowAsIOException(file);
 379             }
 380             return;
 381         }
 382 
 383         boolean r = false;
 384         boolean w = false;
 385         boolean x = false;
 386         for (AccessMode mode: modes) {
 387             switch (mode) {
 388                 case READ : r = true; break;
 389                 case WRITE : w = true; break;
 390                 case EXECUTE : x = true; break;
 391                 default: throw new AssertionError("Should not get here");
 392             }
 393         }
 394 
 395         int mask = 0;
 396         if (r) {
 397             file.checkRead();
 398             mask |= FILE_READ_DATA;
 399         }
 400         if (w) {
 401             file.checkWrite();
 402             mask |= FILE_WRITE_DATA;
 403         }
 404         if (x) {
 405             SecurityManager sm = System.getSecurityManager();
 406             if (sm != null)
 407                 sm.checkExec(file.getPathForPermissionCheck());
 408             mask |= FILE_EXECUTE;
 409         }
 410 
 411         if ((getEffectiveAccess(file) & mask) == 0)
 412             throw new AccessDeniedException(
 413                 file.getPathForExceptionMessage(), null,
 414                 "Effective permissions does not allow requested access");
 415 
 416         // for write access we neeed to check if the DOS readonly attribute
 417         // and if the volume is read-only
 418         if (w) {
 419             try {
 420                 WindowsFileAttributes attrs = WindowsFileAttributes.get(file, true);
 421                 if (!attrs.isDirectory() && attrs.isReadOnly())
 422                     throw new AccessDeniedException(
 423                         file.getPathForExceptionMessage(), null,
 424                         "DOS readonly attribute is set");
 425             } catch (WindowsException exc) {
 426                 exc.rethrowAsIOException(file);
 427             }
 428 
 429             if (WindowsFileStore.create(file).isReadOnly()) {
 430                 throw new AccessDeniedException(
 431                     file.getPathForExceptionMessage(), null, "Read-only file system");
 432             }
 433             return;
 434         }
 435     }
 436 
 437     @Override
 438     public boolean isSameFile(Path obj1, Path obj2) throws IOException {
 439         WindowsPath file1 = WindowsPath.toWindowsPath(obj1);
 440         if (file1.equals(obj2))
 441             return true;
 442         if (obj2 == null)
 443             throw new NullPointerException();
 444         if (!(obj2 instanceof WindowsPath))
 445             return false;
 446         WindowsPath file2 = (WindowsPath)obj2;
 447 
 448         // check security manager access to both files
 449         file1.checkRead();
 450         file2.checkRead();
 451 
 452         // open both files and see if they are the same
 453         long h1 = 0L;
 454         try {
 455             h1 = file1.openForReadAttributeAccess(true);
 456         } catch (WindowsException x) {
 457             x.rethrowAsIOException(file1);
 458         }
 459         try {
 460             WindowsFileAttributes attrs1 = null;
 461             try {
 462                 attrs1 = WindowsFileAttributes.readAttributes(h1);
 463             } catch (WindowsException x) {
 464                 x.rethrowAsIOException(file1);
 465             }
 466             long h2 = 0L;
 467             try {
 468                 h2 = file2.openForReadAttributeAccess(true);
 469             } catch (WindowsException x) {
 470                 x.rethrowAsIOException(file2);
 471             }
 472             try {
 473                 WindowsFileAttributes attrs2 = null;
 474                 try {
 475                     attrs2 = WindowsFileAttributes.readAttributes(h2);
 476                 } catch (WindowsException x) {
 477                     x.rethrowAsIOException(file2);
 478                 }
 479                 return WindowsFileAttributes.isSameFile(attrs1, attrs2);
 480             } finally {
 481                 CloseHandle(h2);
 482             }
 483         } finally {
 484             CloseHandle(h1);
 485         }
 486     }
 487 
 488     @Override
 489     public boolean isHidden(Path obj) throws IOException {
 490         WindowsPath file = WindowsPath.toWindowsPath(obj);
 491         file.checkRead();
 492         WindowsFileAttributes attrs = null;
 493         try {
 494             attrs = WindowsFileAttributes.get(file, true);
 495         } catch (WindowsException x) {
 496             x.rethrowAsIOException(file);
 497         }
 498         // DOS hidden attribute not meaningful when set on directories
 499         if (attrs.isDirectory())
 500             return false;
 501         return attrs.isHidden();
 502     }
 503 
 504     @Override
 505     public FileStore getFileStore(Path obj) throws IOException {
 506         WindowsPath file = WindowsPath.toWindowsPath(obj);
 507         SecurityManager sm = System.getSecurityManager();
 508         if (sm != null) {
 509             sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
 510             file.checkRead();
 511         }
 512         return WindowsFileStore.create(file);
 513     }
 514 
 515 
 516     @Override
 517     public void createDirectory(Path obj, FileAttribute<?>... attrs)
 518         throws IOException
 519     {
 520         WindowsPath dir = WindowsPath.toWindowsPath(obj);
 521         dir.checkWrite();
 522         WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
 523         try {
 524             CreateDirectory(dir.getPathForWin32Calls(), sd.address());
 525         } catch (WindowsException x) {
 526             x.rethrowAsIOException(dir);
 527         } finally {
 528             sd.release();
 529         }
 530     }
 531 
 532     @Override
 533     public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)
 534         throws IOException
 535     {
 536         WindowsPath dir = WindowsPath.toWindowsPath(obj);
 537         dir.checkRead();
 538         if (filter == null)
 539             throw new NullPointerException();
 540         return new WindowsDirectoryStream(dir, filter);
 541     }
 542 
 543     @Override
 544     public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)
 545         throws IOException
 546     {
 547         WindowsPath link = WindowsPath.toWindowsPath(obj1);
 548         WindowsPath target = WindowsPath.toWindowsPath(obj2);
 549         
 550         if (!link.getFileSystem().supportsLinks()) {
 551             throw new UnsupportedOperationException("Symbolic links not supported "
 552                 + "on this operating system");
 553         }
 554 
 555         // no attributes allowed
 556         if (attrs.length > 0) {
 557             WindowsSecurityDescriptor.fromAttribute(attrs);  // may throw NPE or UOE
 558             throw new UnsupportedOperationException("Initial file attributes" +
 559                 "not supported when creating symbolic link");
 560         }
 561 
 562         // permission check
 563         SecurityManager sm = System.getSecurityManager();
 564         if (sm != null) {
 565             sm.checkPermission(new LinkPermission("symbolic"));
 566             link.checkWrite();
 567         }
 568 
 569         /**
 570          * Throw I/O exception for the drive-relative case because Windows
 571          * creates a link with the resolved target for this case.
 572          */
 573         if (target.type() == WindowsPathType.DRIVE_RELATIVE) {
 574             throw new IOException("Cannot create symbolic link to working directory relative target");
 575         }
 576 
 577         /*
 578          * Windows treates symbolic links to directories differently than it
 579          * does to other file types. For that reason we need to check if the
 580          * target is a directory (or a directory junction).
 581          */
 582         WindowsPath resolvedTarget;
 583         if (target.type() == WindowsPathType.RELATIVE) {
 584             WindowsPath parent = link.getParent();
 585             resolvedTarget = (parent == null) ? target : parent.resolve(target);
 586         } else {
 587             resolvedTarget = link.resolve(target);
 588         }
 589         int flags = 0;
 590         try {
 591             WindowsFileAttributes wattrs = WindowsFileAttributes.get(resolvedTarget, false);
 592             if (wattrs.isDirectory() || wattrs.isDirectoryLink())
 593                 flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
 594         } catch (WindowsException x) {
 595             // unable to access target so assume target is not a directory
 596         }
 597 
 598         // create the link
 599         try {
 600             CreateSymbolicLink(link.getPathForWin32Calls(),
 601                                WindowsPath.addPrefixIfNeeded(target.toString()),
 602                                flags);
 603         } catch (WindowsException x) {
 604             if (x.lastError() == ERROR_INVALID_REPARSE_DATA) {
 605                 x.rethrowAsIOException(link, target);
 606             } else {
 607                 x.rethrowAsIOException(link);
 608             }
 609         }
 610     }
 611 
 612     @Override
 613     public void createLink(Path obj1, Path obj2) throws IOException {
 614         WindowsPath link = WindowsPath.toWindowsPath(obj1);
 615         WindowsPath existing = WindowsPath.toWindowsPath(obj2);
 616 
 617         // permission check
 618         SecurityManager sm = System.getSecurityManager();
 619         if (sm != null) {
 620             sm.checkPermission(new LinkPermission("hard"));
 621             link.checkWrite();
 622             existing.checkWrite();
 623         }
 624 
 625         // create hard link
 626         try {
 627             CreateHardLink(link.getPathForWin32Calls(),
 628                            existing.getPathForWin32Calls());
 629         } catch (WindowsException x) {
 630             x.rethrowAsIOException(link, existing);
 631         }
 632     }
 633 
 634     @Override
 635     public Path readSymbolicLink(Path obj1) throws IOException {
 636         WindowsPath link = WindowsPath.toWindowsPath(obj1);
 637         WindowsFileSystem fs = link.getFileSystem();
 638         if (!fs.supportsLinks()) {
 639             throw new UnsupportedOperationException("symbolic links not supported");
 640         }
 641 
 642         // permission check
 643         SecurityManager sm = System.getSecurityManager();
 644         if (sm != null) {
 645             FilePermission perm = new FilePermission(link.getPathForPermissionCheck(),
 646                 SecurityConstants.FILE_READLINK_ACTION);
 647             AccessController.checkPermission(perm);
 648         }
 649 
 650         String target = WindowsLinkSupport.readLink(link);
 651         return WindowsPath.createFromNormalizedPath(fs, target);
 652     }    
 653 }