1 /*
   2  * Copyright (c) 2008, 2013, 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.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.WindowsSecurity.*;
  42 import static sun.nio.fs.WindowsConstants.*;
  43 
  44 public class WindowsFileSystemProvider
  45     extends AbstractFileSystemProvider
  46 {
  47     private static final Unsafe unsafe = Unsafe.getUnsafe();
  48 
  49     private static final String USER_DIR = "user.dir";
  50     private final WindowsFileSystem theFileSystem;
  51 
  52     public WindowsFileSystemProvider() {
  53         theFileSystem = new WindowsFileSystem(this, System.getProperty(USER_DIR));
  54     }
  55 
  56     @Override
  57     public String getScheme() {
  58         return "file";
  59     }
  60 
  61     private void checkUri(URI uri) {
  62         if (!uri.getScheme().equalsIgnoreCase(getScheme()))
  63             throw new IllegalArgumentException("URI does not match this provider");
  64         if (uri.getAuthority() != null)
  65             throw new IllegalArgumentException("Authority component present");
  66         if (uri.getPath() == null)
  67             throw new IllegalArgumentException("Path component is undefined");
  68         if (!uri.getPath().equals("/"))
  69             throw new IllegalArgumentException("Path component should be '/'");
  70         if (uri.getQuery() != null)
  71             throw new IllegalArgumentException("Query component present");
  72         if (uri.getFragment() != null)
  73             throw new IllegalArgumentException("Fragment component present");
  74     }
  75 
  76     @Override
  77     public FileSystem newFileSystem(URI uri, Map<String,?> env)
  78         throws IOException
  79     {
  80         checkUri(uri);
  81         throw new FileSystemAlreadyExistsException();
  82     }
  83 
  84     @Override
  85     public final FileSystem getFileSystem(URI uri) {
  86         checkUri(uri);
  87         return theFileSystem;
  88     }
  89 
  90     @Override
  91     public Path getPath(URI uri) {
  92         return WindowsUriSupport.fromUri(theFileSystem, uri);
  93     }
  94 
  95     @Override
  96     public FileChannel newFileChannel(Path path,
  97                                       Set<? extends OpenOption> options,
  98                                       FileAttribute<?>... attrs)
  99         throws IOException
 100     {
 101         if (path == null)
 102             throw new NullPointerException();
 103         if (!(path instanceof WindowsPath))
 104             throw new ProviderMismatchException();
 105         WindowsPath file = (WindowsPath)path;
 106 
 107         WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
 108         try {
 109             return WindowsChannelFactory
 110                 .newFileChannel(file.getPathForWin32Calls(),
 111                                 file.getPathForPermissionCheck(),
 112                                 options,
 113                                 sd.address());
 114         } catch (WindowsException x) {
 115             x.rethrowAsIOException(file);
 116             return null;
 117         } finally {
 118             if (sd != null)
 119                 sd.release();
 120         }
 121     }
 122 
 123     @Override
 124     public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
 125                                                               Set<? extends OpenOption> options,
 126                                                               ExecutorService executor,
 127                                                               FileAttribute<?>... attrs)
 128         throws IOException
 129     {
 130         if (path == null)
 131             throw new NullPointerException();
 132         if (!(path instanceof WindowsPath))
 133             throw new ProviderMismatchException();
 134         WindowsPath file = (WindowsPath)path;
 135         ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0);
 136         WindowsSecurityDescriptor sd =
 137             WindowsSecurityDescriptor.fromAttribute(attrs);
 138         try {
 139             return WindowsChannelFactory
 140                 .newAsynchronousFileChannel(file.getPathForWin32Calls(),
 141                                             file.getPathForPermissionCheck(),
 142                                             options,
 143                                             sd.address(),
 144                                             pool);
 145         } catch (WindowsException x) {
 146             x.rethrowAsIOException(file);
 147             return null;
 148         } finally {
 149             if (sd != null)
 150                 sd.release();
 151         }
 152     }
 153 
 154     @Override
 155     @SuppressWarnings("unchecked")
 156     public <V extends FileAttributeView> V
 157         getFileAttributeView(Path obj, Class<V> view, LinkOption... options)
 158     {
 159         WindowsPath file = WindowsPath.toWindowsPath(obj);
 160         if (view == null)
 161             throw new NullPointerException();
 162         boolean followLinks = Util.followLinks(options);
 163         if (view == BasicFileAttributeView.class)
 164             return (V) WindowsFileAttributeViews.createBasicView(file, followLinks);
 165         if (view == DosFileAttributeView.class)
 166             return (V) WindowsFileAttributeViews.createDosView(file, followLinks);
 167         if (view == AclFileAttributeView.class)
 168             return (V) new WindowsAclFileAttributeView(file, followLinks);
 169         if (view == FileOwnerAttributeView.class)
 170             return (V) new FileOwnerAttributeViewImpl(
 171                 new WindowsAclFileAttributeView(file, followLinks));
 172         if (view == UserDefinedFileAttributeView.class)
 173             return (V) new WindowsUserDefinedFileAttributeView(file, followLinks);
 174         return (V) null;
 175     }
 176 
 177     @Override
 178     @SuppressWarnings("unchecked")
 179     public <A extends BasicFileAttributes> A readAttributes(Path file,
 180                                                             Class<A> type,
 181                                                             LinkOption... options)
 182         throws IOException
 183     {
 184         Class<? extends BasicFileAttributeView> view;
 185         if (type == BasicFileAttributes.class)
 186             view = BasicFileAttributeView.class;
 187         else if (type == DosFileAttributes.class)
 188             view = DosFileAttributeView.class;
 189         else if (type == null)
 190             throw new NullPointerException();
 191         else
 192             throw new UnsupportedOperationException();
 193         return (A) getFileAttributeView(file, view, options).readAttributes();
 194     }
 195 
 196     @Override
 197     public DynamicFileAttributeView getFileAttributeView(Path obj, String name, LinkOption... options) {
 198         WindowsPath file = WindowsPath.toWindowsPath(obj);
 199         boolean followLinks = Util.followLinks(options);
 200         if (name.equals("basic"))
 201             return WindowsFileAttributeViews.createBasicView(file, followLinks);
 202         if (name.equals("dos"))
 203             return WindowsFileAttributeViews.createDosView(file, followLinks);
 204         if (name.equals("acl"))
 205             return new WindowsAclFileAttributeView(file, followLinks);
 206         if (name.equals("owner"))
 207             return new FileOwnerAttributeViewImpl(
 208                 new WindowsAclFileAttributeView(file, followLinks));
 209         if (name.equals("user"))
 210             return new WindowsUserDefinedFileAttributeView(file, followLinks);
 211         return null;
 212     }
 213 
 214     @Override
 215     public SeekableByteChannel newByteChannel(Path obj,
 216                                               Set<? extends OpenOption> options,
 217                                               FileAttribute<?>... attrs)
 218          throws IOException
 219     {
 220         WindowsPath file = WindowsPath.toWindowsPath(obj);
 221         WindowsSecurityDescriptor sd =
 222             WindowsSecurityDescriptor.fromAttribute(attrs);
 223         try {
 224             return WindowsChannelFactory
 225                 .newFileChannel(file.getPathForWin32Calls(),
 226                                 file.getPathForPermissionCheck(),
 227                                 options,
 228                                 sd.address());
 229         } catch (WindowsException x) {
 230             x.rethrowAsIOException(file);
 231             return null;  // keep compiler happy
 232         } finally {
 233             sd.release();
 234         }
 235     }
 236 
 237     @Override
 238     boolean implDelete(Path obj, boolean failIfNotExists) throws IOException {
 239         WindowsPath file = WindowsPath.toWindowsPath(obj);
 240         file.checkDelete();
 241 
 242         WindowsFileAttributes attrs = null;
 243         try {
 244              // need to know if file is a directory or junction
 245              attrs = WindowsFileAttributes.get(file, false);
 246              if (attrs.isDirectory() || attrs.isDirectoryLink()) {
 247                 RemoveDirectory(file.getPathForWin32Calls());
 248              } else {
 249                 DeleteFile(file.getPathForWin32Calls());
 250              }
 251              return true;
 252         } catch (WindowsException x) {
 253 
 254             // no-op if file does not exist
 255             if (!failIfNotExists &&
 256                 (x.lastError() == ERROR_FILE_NOT_FOUND ||
 257                  x.lastError() == ERROR_PATH_NOT_FOUND)) return false;
 258 
 259             if (attrs != null && attrs.isDirectory()) {
 260                 // ERROR_ALREADY_EXISTS is returned when attempting to delete
 261                 // non-empty directory on SAMBA servers.
 262                 if (x.lastError() == ERROR_DIR_NOT_EMPTY ||
 263                     x.lastError() == ERROR_ALREADY_EXISTS)
 264                 {
 265                     throw new DirectoryNotEmptyException(
 266                         file.getPathForExceptionMessage());
 267                 }
 268             }
 269             x.rethrowAsIOException(file);
 270             return false;
 271         }
 272     }
 273 
 274     @Override
 275     public void copy(Path source, Path target, CopyOption... options)
 276         throws IOException
 277     {
 278         WindowsFileCopy.copy(WindowsPath.toWindowsPath(source),
 279                              WindowsPath.toWindowsPath(target),
 280                              options);
 281     }
 282 
 283     @Override
 284     public void move(Path source, Path target, CopyOption... options)
 285         throws IOException
 286     {
 287         WindowsFileCopy.move(WindowsPath.toWindowsPath(source),
 288                              WindowsPath.toWindowsPath(target),
 289                              options);
 290     }
 291 
 292     /**
 293      * Checks the file security against desired access.
 294      */
 295     private static boolean hasDesiredAccess(WindowsPath file, int rights) throws IOException {
 296         // read security descriptor containing ACL (symlinks are followed)
 297         boolean hasRights = false;
 298         String target = WindowsLinkSupport.getFinalPath(file, true);
 299         NativeBuffer aclBuffer = WindowsAclFileAttributeView
 300             .getFileSecurity(target,
 301                 DACL_SECURITY_INFORMATION
 302                 | OWNER_SECURITY_INFORMATION
 303                 | GROUP_SECURITY_INFORMATION);
 304         try {
 305             hasRights = checkAccessMask(aclBuffer.address(), rights,
 306                 FILE_GENERIC_READ,
 307                 FILE_GENERIC_WRITE,
 308                 FILE_GENERIC_EXECUTE,
 309                 FILE_ALL_ACCESS);
 310         } catch (WindowsException exc) {
 311             exc.rethrowAsIOException(file);
 312         } finally {
 313             aclBuffer.release();
 314         }
 315         return hasRights;
 316     }
 317 
 318     /**
 319      * Checks if the given file(or directory) exists and is readable.
 320      */
 321     private void checkReadAccess(WindowsPath file) throws IOException {
 322         try {
 323             Set<OpenOption> opts = Collections.emptySet();
 324             FileChannel fc = WindowsChannelFactory
 325                 .newFileChannel(file.getPathForWin32Calls(),
 326                                 file.getPathForPermissionCheck(),
 327                                 opts,
 328                                 0L);
 329             fc.close();
 330         } catch (WindowsException exc) {
 331             // Windows errors are very inconsistent when the file is a directory
 332             // (ERROR_PATH_NOT_FOUND returned for root directories for example)
 333             // so we retry by attempting to open it as a directory.
 334             try {
 335                 new WindowsDirectoryStream(file, null).close();
 336             } catch (IOException ioe) {
 337                 // translate and throw original exception
 338                 exc.rethrowAsIOException(file);
 339             }
 340         }
 341     }
 342 
 343     @Override
 344     public void checkAccess(Path obj, AccessMode... modes) throws IOException {
 345         WindowsPath file = WindowsPath.toWindowsPath(obj);
 346 
 347         boolean r = false;
 348         boolean w = false;
 349         boolean x = false;
 350         for (AccessMode mode: modes) {
 351             switch (mode) {
 352                 case READ : r = true; break;
 353                 case WRITE : w = true; break;
 354                 case EXECUTE : x = true; break;
 355                 default: throw new AssertionError("Should not get here");
 356             }
 357         }
 358 
 359         // special-case read access to avoid needing to determine effective
 360         // access to file; default if modes not specified
 361         if (!w && !x) {
 362             checkReadAccess(file);
 363             return;
 364         }
 365 
 366         int mask = 0;
 367         if (r) {
 368             file.checkRead();
 369             mask |= FILE_READ_DATA;
 370         }
 371         if (w) {
 372             file.checkWrite();
 373             mask |= FILE_WRITE_DATA;
 374         }
 375         if (x) {
 376             SecurityManager sm = System.getSecurityManager();
 377             if (sm != null)
 378                 sm.checkExec(file.getPathForPermissionCheck());
 379             mask |= FILE_EXECUTE;
 380         }
 381 
 382         if (!hasDesiredAccess(file, mask))
 383             throw new AccessDeniedException(
 384                 file.getPathForExceptionMessage(), null,
 385                 "Permissions does not allow requested access");
 386 
 387         // for write access we need to check if the DOS readonly attribute
 388         // and if the volume is read-only
 389         if (w) {
 390             try {
 391                 WindowsFileAttributes attrs = WindowsFileAttributes.get(file, true);
 392                 if (!attrs.isDirectory() && attrs.isReadOnly())
 393                     throw new AccessDeniedException(
 394                         file.getPathForExceptionMessage(), null,
 395                         "DOS readonly attribute is set");
 396             } catch (WindowsException exc) {
 397                 exc.rethrowAsIOException(file);
 398             }
 399 
 400             if (WindowsFileStore.create(file).isReadOnly()) {
 401                 throw new AccessDeniedException(
 402                     file.getPathForExceptionMessage(), null, "Read-only file system");
 403             }
 404         }
 405     }
 406 
 407     @Override
 408     public boolean isSameFile(Path obj1, Path obj2) throws IOException {
 409         WindowsPath file1 = WindowsPath.toWindowsPath(obj1);
 410         if (file1.equals(obj2))
 411             return true;
 412         if (obj2 == null)
 413             throw new NullPointerException();
 414         if (!(obj2 instanceof WindowsPath))
 415             return false;
 416         WindowsPath file2 = (WindowsPath)obj2;
 417 
 418         // check security manager access to both files
 419         file1.checkRead();
 420         file2.checkRead();
 421 
 422         // open both files and see if they are the same
 423         long h1 = 0L;
 424         try {
 425             h1 = file1.openForReadAttributeAccess(true);
 426         } catch (WindowsException x) {
 427             x.rethrowAsIOException(file1);
 428         }
 429         try {
 430             WindowsFileAttributes attrs1 = null;
 431             try {
 432                 attrs1 = WindowsFileAttributes.readAttributes(h1);
 433             } catch (WindowsException x) {
 434                 x.rethrowAsIOException(file1);
 435             }
 436             long h2 = 0L;
 437             try {
 438                 h2 = file2.openForReadAttributeAccess(true);
 439             } catch (WindowsException x) {
 440                 x.rethrowAsIOException(file2);
 441             }
 442             try {
 443                 WindowsFileAttributes attrs2 = null;
 444                 try {
 445                     attrs2 = WindowsFileAttributes.readAttributes(h2);
 446                 } catch (WindowsException x) {
 447                     x.rethrowAsIOException(file2);
 448                 }
 449                 return WindowsFileAttributes.isSameFile(attrs1, attrs2);
 450             } finally {
 451                 CloseHandle(h2);
 452             }
 453         } finally {
 454             CloseHandle(h1);
 455         }
 456     }
 457 
 458     @Override
 459     public boolean isHidden(Path obj) throws IOException {
 460         WindowsPath file = WindowsPath.toWindowsPath(obj);
 461         file.checkRead();
 462         WindowsFileAttributes attrs = null;
 463         try {
 464             attrs = WindowsFileAttributes.get(file, true);
 465         } catch (WindowsException x) {
 466             x.rethrowAsIOException(file);
 467         }
 468         // DOS hidden attribute not meaningful when set on directories
 469         if (attrs.isDirectory())
 470             return false;
 471         return attrs.isHidden();
 472     }
 473 
 474     @Override
 475     public FileStore getFileStore(Path obj) throws IOException {
 476         WindowsPath file = WindowsPath.toWindowsPath(obj);
 477         SecurityManager sm = System.getSecurityManager();
 478         if (sm != null) {
 479             sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
 480             file.checkRead();
 481         }
 482         return WindowsFileStore.create(file);
 483     }
 484 
 485 
 486     @Override
 487     public void createDirectory(Path obj, FileAttribute<?>... attrs)
 488         throws IOException
 489     {
 490         WindowsPath dir = WindowsPath.toWindowsPath(obj);
 491         dir.checkWrite();
 492         WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs);
 493         try {
 494             CreateDirectory(dir.getPathForWin32Calls(), sd.address());
 495         } catch (WindowsException x) {
 496             // convert ERROR_ACCESS_DENIED to FileAlreadyExistsException if we can
 497             // verify that the directory exists
 498             if (x.lastError() == ERROR_ACCESS_DENIED) {
 499                 try {
 500                     if (WindowsFileAttributes.get(dir, false).isDirectory())
 501                         throw new FileAlreadyExistsException(dir.toString());
 502                 } catch (WindowsException ignore) { }
 503             }
 504             x.rethrowAsIOException(dir);
 505         } finally {
 506             sd.release();
 507         }
 508     }
 509 
 510     @Override
 511     public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)
 512         throws IOException
 513     {
 514         WindowsPath dir = WindowsPath.toWindowsPath(obj);
 515         dir.checkRead();
 516         if (filter == null)
 517             throw new NullPointerException();
 518         return new WindowsDirectoryStream(dir, filter);
 519     }
 520 
 521     @Override
 522     public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)
 523         throws IOException
 524     {
 525         WindowsPath link = WindowsPath.toWindowsPath(obj1);
 526         WindowsPath target = WindowsPath.toWindowsPath(obj2);
 527 
 528         if (!link.getFileSystem().supportsLinks()) {
 529             throw new UnsupportedOperationException("Symbolic links not supported "
 530                 + "on this operating system");
 531         }
 532 
 533         // no attributes allowed
 534         if (attrs.length > 0) {
 535             WindowsSecurityDescriptor.fromAttribute(attrs);  // may throw NPE or UOE
 536             throw new UnsupportedOperationException("Initial file attributes" +
 537                 "not supported when creating symbolic link");
 538         }
 539 
 540         // permission check
 541         SecurityManager sm = System.getSecurityManager();
 542         if (sm != null) {
 543             sm.checkPermission(new LinkPermission("symbolic"));
 544             link.checkWrite();
 545         }
 546 
 547         /**
 548          * Throw I/O exception for the drive-relative case because Windows
 549          * creates a link with the resolved target for this case.
 550          */
 551         if (target.type() == WindowsPathType.DRIVE_RELATIVE) {
 552             throw new IOException("Cannot create symbolic link to working directory relative target");
 553         }
 554 
 555         /*
 556          * Windows treats symbolic links to directories differently than it
 557          * does to other file types. For that reason we need to check if the
 558          * target is a directory (or a directory junction).
 559          */
 560         WindowsPath resolvedTarget;
 561         if (target.type() == WindowsPathType.RELATIVE) {
 562             WindowsPath parent = link.getParent();
 563             resolvedTarget = (parent == null) ? target : parent.resolve(target);
 564         } else {
 565             resolvedTarget = link.resolve(target);
 566         }
 567         int flags = 0;
 568         try {
 569             WindowsFileAttributes wattrs = WindowsFileAttributes.get(resolvedTarget, false);
 570             if (wattrs.isDirectory() || wattrs.isDirectoryLink())
 571                 flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
 572         } catch (WindowsException x) {
 573             // unable to access target so assume target is not a directory
 574         }
 575 
 576         // create the link
 577         try {
 578             CreateSymbolicLink(link.getPathForWin32Calls(),
 579                                WindowsPath.addPrefixIfNeeded(target.toString()),
 580                                flags);
 581         } catch (WindowsException x) {
 582             if (x.lastError() == ERROR_INVALID_REPARSE_DATA) {
 583                 x.rethrowAsIOException(link, target);
 584             } else {
 585                 x.rethrowAsIOException(link);
 586             }
 587         }
 588     }
 589 
 590     @Override
 591     public void createLink(Path obj1, Path obj2) throws IOException {
 592         WindowsPath link = WindowsPath.toWindowsPath(obj1);
 593         WindowsPath existing = WindowsPath.toWindowsPath(obj2);
 594 
 595         // permission check
 596         SecurityManager sm = System.getSecurityManager();
 597         if (sm != null) {
 598             sm.checkPermission(new LinkPermission("hard"));
 599             link.checkWrite();
 600             existing.checkWrite();
 601         }
 602 
 603         // create hard link
 604         try {
 605             CreateHardLink(link.getPathForWin32Calls(),
 606                            existing.getPathForWin32Calls());
 607         } catch (WindowsException x) {
 608             x.rethrowAsIOException(link, existing);
 609         }
 610     }
 611 
 612     @Override
 613     public Path readSymbolicLink(Path obj1) throws IOException {
 614         WindowsPath link = WindowsPath.toWindowsPath(obj1);
 615         WindowsFileSystem fs = link.getFileSystem();
 616         if (!fs.supportsLinks()) {
 617             throw new UnsupportedOperationException("symbolic links not supported");
 618         }
 619 
 620         // permission check
 621         SecurityManager sm = System.getSecurityManager();
 622         if (sm != null) {
 623             FilePermission perm = new FilePermission(link.getPathForPermissionCheck(),
 624                 SecurityConstants.FILE_READLINK_ACTION);
 625             sm.checkPermission(perm);
 626         }
 627 
 628         String target = WindowsLinkSupport.readLink(link);
 629         return WindowsPath.createFromNormalizedPath(fs, target);
 630     }
 631 }