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 // Android-changed: Complete information about file systems is neither available to regular 363 // apps nor the system server due to SELinux policies. 364 // 365 // UnixPath file = UnixPath.toUnixPath(obj); 366 // SecurityManager sm = System.getSecurityManager(); 367 // if (sm != null) { 368 // sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); 369 // file.checkRead(); 370 // } 371 // return getFileStore(file); 372 throw new SecurityException("getFileStore"); 373 } 374 375 @Override createDirectory(Path obj, FileAttribute<?>... attrs)376 public void createDirectory(Path obj, FileAttribute<?>... attrs) 377 throws IOException 378 { 379 UnixPath dir = UnixPath.toUnixPath(obj); 380 dir.checkWrite(); 381 382 int mode = UnixFileModeAttribute.toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs); 383 try { 384 mkdir(dir, mode); 385 } catch (UnixException x) { 386 if (x.errno() == EISDIR) 387 throw new FileAlreadyExistsException(dir.toString()); 388 x.rethrowAsIOException(dir); 389 } 390 } 391 392 393 @Override newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)394 public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter) 395 throws IOException 396 { 397 UnixPath dir = UnixPath.toUnixPath(obj); 398 dir.checkRead(); 399 if (filter == null) 400 throw new NullPointerException(); 401 402 // can't return SecureDirectoryStream on kernels that don't support openat 403 // or O_NOFOLLOW 404 if (!openatSupported() || O_NOFOLLOW == 0) { 405 try { 406 long ptr = opendir(dir); 407 return new UnixDirectoryStream(dir, ptr, filter); 408 } catch (UnixException x) { 409 if (x.errno() == ENOTDIR) 410 throw new NotDirectoryException(dir.getPathForExceptionMessage()); 411 x.rethrowAsIOException(dir); 412 } 413 } 414 415 // open directory and dup file descriptor for use by 416 // opendir/readdir/closedir 417 int dfd1 = -1; 418 int dfd2 = -1; 419 long dp = 0L; 420 try { 421 dfd1 = open(dir, O_RDONLY, 0); 422 dfd2 = dup(dfd1); 423 dp = fdopendir(dfd1); 424 } catch (UnixException x) { 425 if (dfd1 != -1) 426 UnixNativeDispatcher.close(dfd1); 427 if (dfd2 != -1) 428 UnixNativeDispatcher.close(dfd2); 429 if (x.errno() == UnixConstants.ENOTDIR) 430 throw new NotDirectoryException(dir.getPathForExceptionMessage()); 431 x.rethrowAsIOException(dir); 432 } 433 return new UnixSecureDirectoryStream(dir, dp, dfd2, filter); 434 } 435 436 @Override createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)437 public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs) 438 throws IOException 439 { 440 UnixPath link = UnixPath.toUnixPath(obj1); 441 UnixPath target = UnixPath.toUnixPath(obj2); 442 443 // no attributes supported when creating links 444 if (attrs.length > 0) { 445 UnixFileModeAttribute.toUnixMode(0, attrs); // may throw NPE or UOE 446 throw new UnsupportedOperationException("Initial file attributes" + 447 "not supported when creating symbolic link"); 448 } 449 450 // permission check 451 SecurityManager sm = System.getSecurityManager(); 452 if (sm != null) { 453 sm.checkPermission(new LinkPermission("symbolic")); 454 link.checkWrite(); 455 } 456 457 // create link 458 try { 459 symlink(target.asByteArray(), link); 460 } catch (UnixException x) { 461 x.rethrowAsIOException(link); 462 } 463 } 464 465 @Override createLink(Path obj1, Path obj2)466 public void createLink(Path obj1, Path obj2) throws IOException { 467 UnixPath link = UnixPath.toUnixPath(obj1); 468 UnixPath existing = UnixPath.toUnixPath(obj2); 469 470 // permission check 471 SecurityManager sm = System.getSecurityManager(); 472 if (sm != null) { 473 sm.checkPermission(new LinkPermission("hard")); 474 link.checkWrite(); 475 existing.checkWrite(); 476 } 477 try { 478 link(existing, link); 479 } catch (UnixException x) { 480 x.rethrowAsIOException(link, existing); 481 } 482 } 483 484 @Override readSymbolicLink(Path obj1)485 public Path readSymbolicLink(Path obj1) throws IOException { 486 UnixPath link = UnixPath.toUnixPath(obj1); 487 // permission check 488 SecurityManager sm = System.getSecurityManager(); 489 if (sm != null) { 490 FilePermission perm = new FilePermission(link.getPathForPermissionCheck(), 491 SecurityConstants.FILE_READLINK_ACTION); 492 sm.checkPermission(perm); 493 } 494 try { 495 byte[] target = readlink(link); 496 return new UnixPath(link.getFileSystem(), target); 497 } catch (UnixException x) { 498 if (x.errno() == UnixConstants.EINVAL) 499 throw new NotLinkException(link.getPathForExceptionMessage()); 500 x.rethrowAsIOException(link); 501 return null; // keep compiler happy 502 } 503 } 504 505 /** 506 * Returns a {@code FileTypeDetector} for this platform. 507 */ getFileTypeDetector()508 FileTypeDetector getFileTypeDetector() { 509 return new AbstractFileTypeDetector() { 510 @Override 511 public String implProbeContentType(Path file) { 512 return null; 513 } 514 }; 515 } 516 517 /** 518 * Returns a {@code FileTypeDetector} that chains the given array of file 519 * type detectors. When the {@code implProbeContentType} method is invoked 520 * then each of the detectors is invoked in turn, the result from the 521 * first to detect the file type is returned. 522 */ 523 final FileTypeDetector chain(final AbstractFileTypeDetector... detectors) { 524 return new AbstractFileTypeDetector() { 525 @Override 526 protected String implProbeContentType(Path file) throws IOException { 527 for (AbstractFileTypeDetector detector : detectors) { 528 String result = detector.implProbeContentType(file); 529 if (result != null && !result.isEmpty()) { 530 return result; 531 } 532 } 533 return null; 534 } 535 }; 536 } 537 } 538