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