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