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.file.spi.FileTypeDetector; 31 import java.nio.channels.*; 32 import java.net.URI; 33 import java.util.concurrent.ExecutorService; 34 import java.io.IOException; 35 import java.io.FilePermission; 36 import java.util.*; 37 import java.security.AccessController; 38 39 import sun.nio.ch.ThreadPool; 40 import sun.security.util.SecurityConstants; 41 import static sun.nio.fs.UnixNativeDispatcher.*; 42 import static sun.nio.fs.UnixConstants.*; 43 44 /** 45 * Base implementation of FileSystemProvider 46 */ 47 48 public abstract class UnixFileSystemProvider 49 extends AbstractFileSystemProvider 50 { 51 private static final String USER_DIR = "user.dir"; 52 final UnixFileSystem theFileSystem; 53 54 public UnixFileSystemProvider() { 55 String userDir = System.getProperty(USER_DIR); 56 theFileSystem = newFileSystem(userDir); 57 } 58 59 /** 60 * Constructs a new file system using the given default directory. 61 */ 62 abstract UnixFileSystem newFileSystem(String dir); 63 64 @Override 65 public final String getScheme() { 66 return "file"; 67 } 68 69 private void checkUri(URI uri) { 70 if (!uri.getScheme().equalsIgnoreCase(getScheme())) 71 throw new IllegalArgumentException("URI does not match this provider"); 72 if (uri.getRawAuthority() != null) 73 throw new IllegalArgumentException("Authority component present"); 74 String path = uri.getPath(); 75 if (path == null) 76 throw new IllegalArgumentException("Path component is undefined"); 77 if (!path.equals("/")) 78 throw new IllegalArgumentException("Path component should be '/'"); 79 if (uri.getRawQuery() != null) 80 throw new IllegalArgumentException("Query component present"); 81 if (uri.getRawFragment() != null) 82 throw new IllegalArgumentException("Fragment component present"); 83 } 84 85 @Override 86 public final FileSystem newFileSystem(URI uri, Map<String,?> env) { 87 checkUri(uri); 88 throw new FileSystemAlreadyExistsException(); 89 } 90 91 @Override 92 public final FileSystem getFileSystem(URI uri) { 93 checkUri(uri); 94 return theFileSystem; 95 } 96 97 @Override 98 public Path getPath(URI uri) { 99 return UnixUriUtils.fromUri(theFileSystem, uri); 100 } 101 102 UnixPath checkPath(Path obj) { 103 if (obj == null) 104 throw new NullPointerException(); 105 if (!(obj instanceof UnixPath)) 106 throw new ProviderMismatchException(); 107 return (UnixPath)obj; 108 } 109 110 @Override 111 @SuppressWarnings("unchecked") 112 public <V extends FileAttributeView> V getFileAttributeView(Path obj, 113 Class<V> type, 114 LinkOption... options) 115 { 116 UnixPath file = UnixPath.toUnixPath(obj); 117 boolean followLinks = Util.followLinks(options); 118 if (type == BasicFileAttributeView.class) 119 return (V) UnixFileAttributeViews.createBasicView(file, followLinks); 120 if (type == PosixFileAttributeView.class) 121 return (V) UnixFileAttributeViews.createPosixView(file, followLinks); 122 if (type == FileOwnerAttributeView.class) 123 return (V) UnixFileAttributeViews.createOwnerView(file, followLinks); 124 if (type == null) 125 throw new NullPointerException(); 126 return (V) null; 127 } 128 129 @Override 130 @SuppressWarnings("unchecked") 131 public <A extends BasicFileAttributes> A readAttributes(Path file, 132 Class<A> type, 133 LinkOption... options) 134 throws IOException 135 { 136 Class<? extends BasicFileAttributeView> view; 137 if (type == BasicFileAttributes.class) 138 view = BasicFileAttributeView.class; 139 else if (type == PosixFileAttributes.class) 140 view = PosixFileAttributeView.class; 141 else if (type == null) 142 throw new NullPointerException(); 143 else 144 throw new UnsupportedOperationException(); 145 return (A) getFileAttributeView(file, view, options).readAttributes(); 146 } 147 148 @Override 149 protected DynamicFileAttributeView getFileAttributeView(Path obj, 150 String name, 151 LinkOption... options) 152 { 153 UnixPath file = UnixPath.toUnixPath(obj); 154 boolean followLinks = Util.followLinks(options); 155 if (name.equals("basic")) 156 return UnixFileAttributeViews.createBasicView(file, followLinks); 157 if (name.equals("posix")) 158 return UnixFileAttributeViews.createPosixView(file, followLinks); 159 if (name.equals("unix")) 160 return UnixFileAttributeViews.createUnixView(file, followLinks); 161 if (name.equals("owner")) 162 return UnixFileAttributeViews.createOwnerView(file, followLinks); 163 return null; 164 } 165 166 @Override 167 public FileChannel newFileChannel(Path obj, 168 Set<? extends OpenOption> options, 169 FileAttribute<?>... attrs) 170 throws IOException 171 { 172 UnixPath file = checkPath(obj); 173 int mode = UnixFileModeAttribute 174 .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs); 175 try { 176 return UnixChannelFactory.newFileChannel(file, options, mode); 177 } catch (UnixException x) { 178 x.rethrowAsIOException(file); 179 return null; 180 } 181 } 182 183 @Override 184 public AsynchronousFileChannel newAsynchronousFileChannel(Path obj, 185 Set<? extends OpenOption> options, 186 ExecutorService executor, 187 FileAttribute<?>... attrs) throws IOException 188 { 189 UnixPath file = checkPath(obj); 190 int mode = UnixFileModeAttribute 191 .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs); 192 ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0); 193 try { 194 return UnixChannelFactory 195 .newAsynchronousFileChannel(file, options, mode, pool); 196 } catch (UnixException x) { 197 x.rethrowAsIOException(file); 198 return null; 199 } 200 } 201 202 203 @Override 204 public SeekableByteChannel newByteChannel(Path obj, 205 Set<? extends OpenOption> options, 206 FileAttribute<?>... attrs) 207 throws IOException 208 { 209 UnixPath file = UnixPath.toUnixPath(obj); 210 int mode = UnixFileModeAttribute 211 .toUnixMode(UnixFileModeAttribute.ALL_READWRITE, attrs); 212 try { 213 return UnixChannelFactory.newFileChannel(file, options, mode); 214 } catch (UnixException x) { 215 x.rethrowAsIOException(file); 216 return null; // keep compiler happy 217 } 218 } 219 220 @Override 221 boolean implDelete(Path obj, boolean failIfNotExists) throws IOException { 222 UnixPath file = UnixPath.toUnixPath(obj); 223 file.checkDelete(); 224 225 // need file attributes to know if file is directory 226 UnixFileAttributes attrs = null; 227 try { 228 attrs = UnixFileAttributes.get(file, false); 229 if (attrs.isDirectory()) { 230 rmdir(file); 231 } else { 232 unlink(file); 233 } 234 return true; 235 } catch (UnixException x) { 236 // no-op if file does not exist 237 if (!failIfNotExists && x.errno() == ENOENT) 238 return false; 239 240 // DirectoryNotEmptyException if not empty 241 if (attrs != null && attrs.isDirectory() && 242 (x.errno() == EEXIST || x.errno() == ENOTEMPTY)) 243 throw new DirectoryNotEmptyException(file.getPathForExceptionMessage()); 244 245 x.rethrowAsIOException(file); 246 return false; 247 } 248 } 249 250 @Override 251 public void copy(Path source, Path target, CopyOption... options) 252 throws IOException 253 { 254 UnixCopyFile.copy(UnixPath.toUnixPath(source), 255 UnixPath.toUnixPath(target), 256 options); 257 } 258 259 @Override 260 public void move(Path source, Path target, CopyOption... options) 261 throws IOException 262 { 263 UnixCopyFile.move(UnixPath.toUnixPath(source), 264 UnixPath.toUnixPath(target), 265 options); 266 } 267 268 @Override 269 public void checkAccess(Path obj, AccessMode... modes) throws IOException { 270 UnixPath file = UnixPath.toUnixPath(obj); 271 boolean e = false; 272 boolean r = false; 273 boolean w = false; 274 boolean x = false; 275 276 if (modes.length == 0) { 277 e = true; 278 } else { 279 for (AccessMode mode: modes) { 280 switch (mode) { 281 case READ : r = true; break; 282 case WRITE : w = true; break; 283 case EXECUTE : x = true; break; 284 default: throw new AssertionError("Should not get here"); 285 } 286 } 287 } 288 289 int mode = 0; 290 if (e || r) { 291 file.checkRead(); 292 mode |= (r) ? R_OK : F_OK; 293 } 294 if (w) { 295 file.checkWrite(); 296 mode |= W_OK; 297 } 298 if (x) { 299 SecurityManager sm = System.getSecurityManager(); 300 if (sm != null) { 301 // not cached 302 sm.checkExec(file.getPathForPermissionCheck()); 303 } 304 mode |= X_OK; 305 } 306 try { 307 access(file, mode); 308 } catch (UnixException exc) { 309 exc.rethrowAsIOException(file); 310 } 311 } 312 313 @Override 314 public boolean isSameFile(Path obj1, Path obj2) throws IOException { 315 UnixPath file1 = UnixPath.toUnixPath(obj1); 316 if (file1.equals(obj2)) 317 return true; 318 if (obj2 == null) 319 throw new NullPointerException(); 320 if (!(obj2 instanceof UnixPath)) 321 return false; 322 UnixPath file2 = (UnixPath)obj2; 323 324 // check security manager access to both files 325 file1.checkRead(); 326 file2.checkRead(); 327 328 UnixFileAttributes attrs1; 329 UnixFileAttributes attrs2; 330 try { 331 attrs1 = UnixFileAttributes.get(file1, true); 332 } catch (UnixException x) { 333 x.rethrowAsIOException(file1); 334 return false; // keep compiler happy 335 } 336 try { 337 attrs2 = UnixFileAttributes.get(file2, true); 338 } catch (UnixException x) { 339 x.rethrowAsIOException(file2); 340 return false; // keep compiler happy 341 } 342 return attrs1.isSameFile(attrs2); 343 } 344 345 @Override 346 public boolean isHidden(Path obj) { 347 UnixPath file = UnixPath.toUnixPath(obj); 348 file.checkRead(); 349 UnixPath name = file.getFileName(); 350 if (name == null) 351 return false; 352 return (name.asByteArray()[0] == '.'); 353 } 354 355 /** 356 * Returns a FileStore to represent the file system where the given file 357 * reside. 358 */ 359 abstract FileStore getFileStore(UnixPath path) throws IOException; 360 361 @Override 362 public FileStore getFileStore(Path obj) throws IOException { 363 UnixPath file = UnixPath.toUnixPath(obj); 364 SecurityManager sm = System.getSecurityManager(); 365 if (sm != null) { 366 sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); 367 file.checkRead(); 368 } 369 return getFileStore(file); 370 } 371 372 @Override 373 public void createDirectory(Path obj, FileAttribute<?>... attrs) 374 throws IOException 375 { 376 UnixPath dir = UnixPath.toUnixPath(obj); 377 dir.checkWrite(); 378 379 int mode = UnixFileModeAttribute.toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs); 380 try { 381 mkdir(dir, mode); 382 } catch (UnixException x) { 383 if (x.errno() == EISDIR) 384 throw new FileAlreadyExistsException(dir.toString()); 385 x.rethrowAsIOException(dir); 386 } 387 } 388 389 390 @Override 391 public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter) 392 throws IOException 393 { 394 UnixPath dir = UnixPath.toUnixPath(obj); 395 dir.checkRead(); 396 if (filter == null) 397 throw new NullPointerException(); 398 399 // can't return SecureDirectoryStream on kernels that don't support openat 400 // or O_NOFOLLOW 401 if (!openatSupported() || O_NOFOLLOW == 0) { 402 try { 403 long ptr = opendir(dir); 404 return new UnixDirectoryStream(dir, ptr, filter); 405 } catch (UnixException x) { 406 if (x.errno() == ENOTDIR) 407 throw new NotDirectoryException(dir.getPathForExceptionMessage()); 408 x.rethrowAsIOException(dir); 409 } 410 } 411 412 // open directory and dup file descriptor for use by 413 // opendir/readdir/closedir 414 int dfd1 = -1; 415 int dfd2 = -1; 416 long dp = 0L; 417 try { 418 dfd1 = open(dir, O_RDONLY, 0); 419 dfd2 = dup(dfd1); 420 dp = fdopendir(dfd1); 421 } catch (UnixException x) { 422 if (dfd1 != -1) 423 UnixNativeDispatcher.close(dfd1); 424 if (dfd2 != -1) 425 UnixNativeDispatcher.close(dfd2); 426 if (x.errno() == UnixConstants.ENOTDIR) 427 throw new NotDirectoryException(dir.getPathForExceptionMessage()); 428 x.rethrowAsIOException(dir); 429 } 430 return new UnixSecureDirectoryStream(dir, dp, dfd2, filter); 431 } 432 433 @Override 434 public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs) 435 throws IOException 436 { 437 UnixPath link = UnixPath.toUnixPath(obj1); 438 UnixPath target = UnixPath.toUnixPath(obj2); 439 440 // no attributes supported when creating links 441 if (attrs.length > 0) { 442 UnixFileModeAttribute.toUnixMode(0, attrs); // may throw NPE or UOE 443 throw new UnsupportedOperationException("Initial file attributes" + 444 "not supported when creating symbolic link"); 445 } 446 447 // permission check 448 SecurityManager sm = System.getSecurityManager(); 449 if (sm != null) { 450 sm.checkPermission(new LinkPermission("symbolic")); 451 link.checkWrite(); 452 } 453 454 // create link 455 try { 456 symlink(target.asByteArray(), link); 457 } catch (UnixException x) { 458 x.rethrowAsIOException(link); 459 } 460 } 461 462 @Override 463 public void createLink(Path obj1, Path obj2) throws IOException { 464 UnixPath link = UnixPath.toUnixPath(obj1); 465 UnixPath existing = UnixPath.toUnixPath(obj2); 466 467 // permission check 468 SecurityManager sm = System.getSecurityManager(); 469 if (sm != null) { 470 sm.checkPermission(new LinkPermission("hard")); 471 link.checkWrite(); 472 existing.checkWrite(); 473 } 474 try { 475 link(existing, link); 476 } catch (UnixException x) { 477 x.rethrowAsIOException(link, existing); 478 } 479 } 480 481 @Override 482 public Path readSymbolicLink(Path obj1) throws IOException { 483 UnixPath link = UnixPath.toUnixPath(obj1); 484 // permission check 485 SecurityManager sm = System.getSecurityManager(); 486 if (sm != null) { 487 FilePermission perm = new FilePermission(link.getPathForPermissionCheck(), 488 SecurityConstants.FILE_READLINK_ACTION); 489 sm.checkPermission(perm); 490 } 491 try { 492 byte[] target = readlink(link); 493 return new UnixPath(link.getFileSystem(), target); 494 } catch (UnixException x) { 495 if (x.errno() == UnixConstants.EINVAL) 496 throw new NotLinkException(link.getPathForExceptionMessage()); 497 x.rethrowAsIOException(link); 498 return null; // keep compiler happy 499 } 500 } 501 502 @Override 503 public final boolean isDirectory(Path obj) { 504 UnixPath file = UnixPath.toUnixPath(obj); 505 file.checkRead(); 506 int mode = UnixNativeDispatcher.stat(file); 507 return ((mode & UnixConstants.S_IFMT) == UnixConstants.S_IFDIR); 508 } 509 510 @Override 511 public final boolean isRegularFile(Path obj) { 512 UnixPath file = UnixPath.toUnixPath(obj); 513 file.checkRead(); 514 int mode = UnixNativeDispatcher.stat(file); 515 return ((mode & UnixConstants.S_IFMT) == UnixConstants.S_IFREG); 516 } 517 518 @Override 519 public final boolean exists(Path obj) { 520 UnixPath file = UnixPath.toUnixPath(obj); 521 file.checkRead(); 522 return UnixNativeDispatcher.exists(file); 523 } 524 525 /** 526 * Returns a {@code FileTypeDetector} for this platform. 527 */ 528 FileTypeDetector getFileTypeDetector() { 529 return new AbstractFileTypeDetector() { 530 @Override 531 public String implProbeContentType(Path file) { 532 return null; 533 } 534 }; 535 } 536 537 /** 538 * Returns a {@code FileTypeDetector} that chains the given array of file 539 * type detectors. When the {@code implProbeContentType} method is invoked 540 * then each of the detectors is invoked in turn, the result from the 541 * first to detect the file type is returned. 542 */ 543 final FileTypeDetector chain(final AbstractFileTypeDetector... detectors) { 544 return new AbstractFileTypeDetector() { 545 @Override 546 protected String implProbeContentType(Path file) throws IOException { 547 for (AbstractFileTypeDetector detector : detectors) { 548 String result = detector.implProbeContentType(file); 549 if (result != null && !result.isEmpty()) { 550 return result; 551 } 552 } 553 return null; 554 } 555 }; 556 } 557 }