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 }