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