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 }