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