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