1 /*
   2  * Copyright (c) 2008, 2012, 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     /**
  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.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
  85     public final FileSystem newFileSystem(URI uri, Map<String,?> env) {
  86         checkUri(uri);
  87         throw new FileSystemAlreadyExistsException();
  88     }
  89 
  90     @Override
  91     public final FileSystem getFileSystem(URI uri) {
  92         checkUri(uri);
  93         return theFileSystem;
  94     }
  95 
  96     @Override
  97     public Path getPath(URI uri) {
  98         return UnixUriUtils.fromUri(theFileSystem, uri);
  99     }
 100 
 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")
 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")
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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      */
 358     abstract FileStore getFileStore(UnixPath path) throws IOException;
 359 
 360     @Override
 361     public FileStore getFileStore(Path obj) throws IOException {
 362         UnixPath file = UnixPath.toUnixPath(obj);
 363         SecurityManager sm = System.getSecurityManager();
 364         if (sm != null) {
 365             sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
 366             file.checkRead();
 367         }
 368         return getFileStore(file);
 369     }
 370 
 371     @Override
 372     public void createDirectory(Path obj, FileAttribute<?>... attrs)
 373         throws IOException
 374     {
 375         UnixPath dir = UnixPath.toUnixPath(obj);
 376         dir.checkWrite();
 377 
 378         int mode = UnixFileModeAttribute
 379             .toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs);
 380         try {
 381             mkdir(dir, mode);
 382         } catch (UnixException x) {
 383             x.rethrowAsIOException(dir);
 384         }
 385     }
 386 
 387 
 388     @Override
 389     public DirectoryStream<Path> newDirectoryStream(Path obj, DirectoryStream.Filter<? super Path> filter)
 390         throws IOException
 391     {
 392         UnixPath dir = UnixPath.toUnixPath(obj);
 393         dir.checkRead();
 394         if (filter == null)
 395             throw new NullPointerException();
 396 
 397         // can't return SecureDirectoryStream on kernels that don't support
 398         // openat, etc.
 399         if (!supportsAtSysCalls() || !supportsNoFollowLinks()) {
 400             try {
 401                 long ptr = opendir(dir);
 402                 return new UnixDirectoryStream(dir, ptr, filter);
 403             } catch (UnixException x) {
 404                 if (x.errno() == ENOTDIR)
 405                     throw new NotDirectoryException(dir.getPathForExceptionMessage());
 406                 x.rethrowAsIOException(dir);
 407             }
 408         }
 409 
 410         // open directory and dup file descriptor for use by
 411         // opendir/readdir/closedir
 412         int dfd1 = -1;
 413         int dfd2 = -1;
 414         long dp = 0L;
 415         try {
 416             dfd1 = open(dir, O_RDONLY, 0);
 417             dfd2 = dup(dfd1);
 418             dp = fdopendir(dfd1);
 419         } catch (UnixException x) {
 420             if (dfd1 != -1)
 421                 UnixNativeDispatcher.close(dfd1);
 422             if (dfd2 != -1)
 423                 UnixNativeDispatcher.close(dfd2);
 424             if (x.errno() == UnixConstants.ENOTDIR)
 425                 throw new NotDirectoryException(dir.getPathForExceptionMessage());
 426             x.rethrowAsIOException(dir);
 427         }
 428         return new UnixSecureDirectoryStream(dir, dp, dfd2, filter);
 429     }
 430 
 431     @Override
 432     public void createSymbolicLink(Path obj1, Path obj2, FileAttribute<?>... attrs)
 433         throws IOException
 434     {
 435         UnixPath link = UnixPath.toUnixPath(obj1);
 436         UnixPath target = UnixPath.toUnixPath(obj2);
 437 
 438         // no attributes supported when creating links
 439         if (attrs.length > 0) {
 440             UnixFileModeAttribute.toUnixMode(0, attrs);  // may throw NPE or UOE
 441             throw new UnsupportedOperationException("Initial file attributes" +
 442                 "not supported when creating symbolic link");
 443         }
 444 
 445         // permission check
 446         SecurityManager sm = System.getSecurityManager();
 447         if (sm != null) {
 448             sm.checkPermission(new LinkPermission("symbolic"));
 449             link.checkWrite();
 450         }
 451 
 452         // create link
 453         try {
 454             symlink(target.asByteArray(), link);
 455         } catch (UnixException x) {
 456             x.rethrowAsIOException(link);
 457         }
 458     }
 459 
 460     @Override
 461     public void createLink(Path obj1, Path obj2) throws IOException {
 462         UnixPath link = UnixPath.toUnixPath(obj1);
 463         UnixPath existing = UnixPath.toUnixPath(obj2);
 464 
 465         // permission check
 466         SecurityManager sm = System.getSecurityManager();
 467         if (sm != null) {
 468             sm.checkPermission(new LinkPermission("hard"));
 469             link.checkWrite();
 470             existing.checkWrite();
 471         }
 472         try {
 473             link(existing, link);
 474         } catch (UnixException x) {
 475             x.rethrowAsIOException(link, existing);
 476         }
 477     }
 478 
 479     @Override
 480     public Path readSymbolicLink(Path obj1) throws IOException {
 481         UnixPath link = UnixPath.toUnixPath(obj1);
 482         // permission check
 483         SecurityManager sm = System.getSecurityManager();
 484         if (sm != null) {
 485             FilePermission perm = new FilePermission(link.getPathForPermissionCheck(),
 486                 SecurityConstants.FILE_READLINK_ACTION);
 487             AccessController.checkPermission(perm);
 488         }
 489         try {
 490             byte[] target = readlink(link);
 491             return new UnixPath(link.getFileSystem(), target);
 492         } catch (UnixException x) {
 493            if (x.errno() == UnixConstants.EINVAL)
 494                 throw new NotLinkException(link.getPathForExceptionMessage());
 495             x.rethrowAsIOException(link);
 496             return null;    // keep compiler happy
 497         }
 498     }
 499 
 500     /**
 501      * Returns a {@code FileTypeDetector} for this platform.
 502      */
 503     FileTypeDetector getFileTypeDetector() {
 504         return new AbstractFileTypeDetector() {
 505             @Override
 506             public String implProbeContentType(Path file) {
 507                 return null;
 508             }
 509         };
 510     }
 511 
 512 }