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