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 }