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 }