| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.jimfs; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ImmutableSortedSet; |
| import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.nio.file.FileStore; |
| import java.nio.file.FileSystem; |
| import java.nio.file.Path; |
| import java.nio.file.PathMatcher; |
| import java.nio.file.WatchService; |
| import java.nio.file.attribute.UserPrincipalLookupService; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import org.checkerframework.checker.nullness.compatqual.NullableDecl; |
| |
| /** |
| * {@link FileSystem} implementation for Jimfs. Most behavior for the file system is implemented by |
| * its {@linkplain #getDefaultView() default file system view}. |
| * |
| * <h3>Overview of file system design</h3> |
| * |
| * {@link com.google.common.jimfs.JimfsFileSystem JimfsFileSystem} instances are created by {@link |
| * com.google.common.jimfs.JimfsFileSystems JimfsFileSystems} using a user-provided {@link |
| * com.google.common.jimfs.Configuration Configuration}. The configuration is used to create the |
| * various classes that implement the file system with the correct settings and to create the file |
| * system root directories and working directory. The file system is then used to create the {@code |
| * Path} objects that all file system operations use. |
| * |
| * <p>Once created, the primary entry points to the file system are {@link |
| * com.google.common.jimfs.JimfsFileSystemProvider JimfsFileSystemProvider}, which handles calls to |
| * methods in {@link java.nio.file.Files}, and {@link |
| * com.google.common.jimfs.JimfsSecureDirectoryStream JimfsSecureDirectoryStream}, which provides |
| * methods that are similar to those of the file system provider but which treat relative paths as |
| * relative to the stream's directory rather than the file system's working directory. |
| * |
| * <p>The implementation of the methods on both of those classes is handled by the {@link |
| * com.google.common.jimfs.FileSystemView FileSystemView} class, which acts as a view of the file |
| * system with a specific working directory. The file system provider uses the file system's default |
| * view, while each secure directory stream uses a view specific to that stream. |
| * |
| * <p>File system views make use of the file system's singleton {@link |
| * com.google.common.jimfs.JimfsFileStore JimfsFileStore} which handles file creation, storage and |
| * attributes. The file store delegates to several other classes to handle each of these: |
| * |
| * <ul> |
| * <li>{@link com.google.common.jimfs.FileFactory FileFactory} handles creation of new file |
| * objects. |
| * <li>{@link com.google.common.jimfs.HeapDisk HeapDisk} handles allocation of blocks to {@link |
| * RegularFile RegularFile} instances. |
| * <li>{@link com.google.common.jimfs.FileTree FileTree} stores the root of the file hierarchy and |
| * handles file lookup. |
| * <li>{@link com.google.common.jimfs.AttributeService AttributeService} handles file attributes, |
| * using a set of {@link com.google.common.jimfs.AttributeProvider AttributeProvider} |
| * implementations to handle each supported file attribute view. |
| * </ul> |
| * |
| * <h3>Paths</h3> |
| * |
| * The implementation of {@link java.nio.file.Path} for the file system is {@link |
| * com.google.common.jimfs.JimfsPath JimfsPath}. Paths are created by a {@link |
| * com.google.common.jimfs.PathService PathService} with help from the file system's configured |
| * {@link com.google.common.jimfs.PathType PathType}. |
| * |
| * <p>Paths are made up of {@link com.google.common.jimfs.Name Name} objects, which also serve as |
| * the file names in directories. A name has two forms: |
| * |
| * <ul> |
| * <li>The <b>display form</b> is used in {@code Path} for {@code toString()}. It is also used for |
| * determining the equality and sort order of {@code Path} objects for most file systems. |
| * <li>The <b>canonical form</b> is used for equality of two {@code Name} objects. This affects |
| * the notion of name equality in the file system itself for file lookup. A file system may be |
| * configured to use the canonical form of the name for path equality (a Windows-like file |
| * system configuration does this, as the real Windows file system implementation uses |
| * case-insensitive equality for its path objects. |
| * </ul> |
| * |
| * <p>The canonical form of a name is created by applying a series of {@linkplain PathNormalization |
| * normalizations} to the original string. These normalization may be either a Unicode normalization |
| * (e.g. NFD) or case folding normalization for case-insensitivity. Normalizations may also be |
| * applied to the display form of a name, but this is currently only done for a Mac OS X type |
| * configuration. |
| * |
| * <h3>Files</h3> |
| * |
| * All files in the file system are an instance of {@link com.google.common.jimfs.File File}. A file |
| * object contains both the file's attributes and content. |
| * |
| * <p>There are three types of files: |
| * |
| * <ul> |
| * <li>{@link Directory Directory} - contains a table linking file names to {@linkplain |
| * com.google.common.jimfs.DirectoryEntry directory entries}. |
| * <li>{@link RegularFile RegularFile} - an in-memory store for raw bytes. |
| * <li>{@link com.google.common.jimfs.SymbolicLink SymbolicLink} - contains a path. |
| * </ul> |
| * |
| * <p>{@link com.google.common.jimfs.JimfsFileChannel JimfsFileChannel}, {@link |
| * com.google.common.jimfs.JimfsInputStream JimfsInputStream} and {@link |
| * com.google.common.jimfs.JimfsOutputStream JimfsOutputStream} implement the standard |
| * channel/stream APIs for regular files. |
| * |
| * <p>{@link com.google.common.jimfs.JimfsSecureDirectoryStream JimfsSecureDirectoryStream} handles |
| * reading the entries of a directory. The secure directory stream additionally contains a {@code |
| * FileSystemView} with its directory as the working directory, allowing for operations relative to |
| * the actual directory file rather than just the path to the file. This allows the operations to |
| * continue to work as expected even if the directory is moved. |
| * |
| * <p>A directory can be watched for changes using the {@link java.nio.file.WatchService} |
| * implementation, {@link com.google.common.jimfs.PollingWatchService PollingWatchService}. |
| * |
| * <h3>Regular files</h3> |
| * |
| * {@link RegularFile RegularFile} makes use of a singleton {@link com.google.common.jimfs.HeapDisk |
| * HeapDisk}. A disk is a resizable factory and cache for fixed size blocks of memory. These blocks |
| * are allocated to files as needed and returned to the disk when a file is deleted or truncated. |
| * When cached free blocks are available, those blocks are allocated to files first. If more blocks |
| * are needed, they are created. |
| * |
| * <h3>Linking</h3> |
| * |
| * When a file is mapped to a file name in a directory table, it is <i>linked</i>. Each type of file |
| * has different rules governing how it is linked. |
| * |
| * <ul> |
| * <li>Directory - A directory has two or more links to it. The first is the link from its parent |
| * directory to it. This link is the name of the directory. The second is the <i>self</i> link |
| * (".") which links the directory to itself. The directory may also have any number of |
| * additional <i>parent</i> links ("..") from child directories back to it. |
| * <li>Regular file - A regular file has one link from its parent directory by default. However, |
| * regular files are also allowed to have any number of additional user-created hard links, |
| * from the same directory with different names and/or from other directories with any names. |
| * <li>Symbolic link - A symbolic link can only have one link, from its parent directory. |
| * </ul> |
| * |
| * <h3>Thread safety</h3> |
| * |
| * All file system operations should be safe in a multithreaded environment. The file hierarchy |
| * itself is protected by a file system level read-write lock. This ensures safety of all |
| * modifications to directory tables as well as atomicity of operations like file moves. Regular |
| * files are each protected by a read-write lock which is obtained for each read or write operation. |
| * File attributes are protected by synchronization on the file object itself. |
| * |
| * @author Colin Decker |
| */ |
| final class JimfsFileSystem extends FileSystem { |
| |
| private final JimfsFileSystemProvider provider; |
| private final URI uri; |
| |
| private final JimfsFileStore fileStore; |
| private final PathService pathService; |
| |
| private final UserPrincipalLookupService userLookupService = new UserLookupService(true); |
| |
| private final FileSystemView defaultView; |
| |
| private final WatchServiceConfiguration watchServiceConfig; |
| |
| JimfsFileSystem( |
| JimfsFileSystemProvider provider, |
| URI uri, |
| JimfsFileStore fileStore, |
| PathService pathService, |
| FileSystemView defaultView, |
| WatchServiceConfiguration watchServiceConfig) { |
| this.provider = checkNotNull(provider); |
| this.uri = checkNotNull(uri); |
| this.fileStore = checkNotNull(fileStore); |
| this.pathService = checkNotNull(pathService); |
| this.defaultView = checkNotNull(defaultView); |
| this.watchServiceConfig = checkNotNull(watchServiceConfig); |
| } |
| |
| @Override |
| public JimfsFileSystemProvider provider() { |
| return provider; |
| } |
| |
| /** Returns the URI for this file system. */ |
| public URI getUri() { |
| return uri; |
| } |
| |
| /** Returns the default view for this file system. */ |
| public FileSystemView getDefaultView() { |
| return defaultView; |
| } |
| |
| @Override |
| public String getSeparator() { |
| return pathService.getSeparator(); |
| } |
| |
| @SuppressWarnings("unchecked") // safe cast of immutable set |
| @Override |
| public ImmutableSortedSet<Path> getRootDirectories() { |
| ImmutableSortedSet.Builder<JimfsPath> builder = ImmutableSortedSet.orderedBy(pathService); |
| for (Name name : fileStore.getRootDirectoryNames()) { |
| builder.add(pathService.createRoot(name)); |
| } |
| return (ImmutableSortedSet<Path>) (ImmutableSortedSet<?>) builder.build(); |
| } |
| |
| /** Returns the working directory path for this file system. */ |
| public JimfsPath getWorkingDirectory() { |
| return defaultView.getWorkingDirectoryPath(); |
| } |
| |
| /** Returns the path service for this file system. */ |
| @VisibleForTesting |
| PathService getPathService() { |
| return pathService; |
| } |
| |
| /** Returns the file store for this file system. */ |
| public JimfsFileStore getFileStore() { |
| return fileStore; |
| } |
| |
| @Override |
| public ImmutableSet<FileStore> getFileStores() { |
| fileStore.state().checkOpen(); |
| return ImmutableSet.<FileStore>of(fileStore); |
| } |
| |
| @Override |
| public ImmutableSet<String> supportedFileAttributeViews() { |
| return fileStore.supportedFileAttributeViews(); |
| } |
| |
| @Override |
| public JimfsPath getPath(String first, String... more) { |
| fileStore.state().checkOpen(); |
| return pathService.parsePath(first, more); |
| } |
| |
| /** Gets the URI of the given path in this file system. */ |
| public URI toUri(JimfsPath path) { |
| fileStore.state().checkOpen(); |
| return pathService.toUri(uri, path.toAbsolutePath()); |
| } |
| |
| /** Converts the given URI into a path in this file system. */ |
| public JimfsPath toPath(URI uri) { |
| fileStore.state().checkOpen(); |
| return pathService.fromUri(uri); |
| } |
| |
| @Override |
| public PathMatcher getPathMatcher(String syntaxAndPattern) { |
| fileStore.state().checkOpen(); |
| return pathService.createPathMatcher(syntaxAndPattern); |
| } |
| |
| @Override |
| public UserPrincipalLookupService getUserPrincipalLookupService() { |
| fileStore.state().checkOpen(); |
| return userLookupService; |
| } |
| |
| @Override |
| public WatchService newWatchService() throws IOException { |
| return watchServiceConfig.newWatchService(defaultView, pathService); |
| } |
| |
| @NullableDecl private ExecutorService defaultThreadPool; |
| |
| /** |
| * Returns a default thread pool to use for asynchronous file channels when users do not provide |
| * an executor themselves. (This is required by the spec of newAsynchronousFileChannel in |
| * FileSystemProvider.) |
| */ |
| public synchronized ExecutorService getDefaultThreadPool() { |
| if (defaultThreadPool == null) { |
| defaultThreadPool = |
| Executors.newCachedThreadPool( |
| new ThreadFactoryBuilder() |
| .setDaemon(true) |
| .setNameFormat("JimfsFileSystem-" + uri.getHost() + "-defaultThreadPool-%s") |
| .build()); |
| |
| // ensure thread pool is closed when file system is closed |
| fileStore |
| .state() |
| .register( |
| new Closeable() { |
| @Override |
| public void close() { |
| defaultThreadPool.shutdown(); |
| } |
| }); |
| } |
| return defaultThreadPool; |
| } |
| |
| /** |
| * Returns {@code false}; currently, cannot create a read-only file system. |
| * |
| * @return {@code false}, always |
| */ |
| @Override |
| public boolean isReadOnly() { |
| return false; |
| } |
| |
| @Override |
| public boolean isOpen() { |
| return fileStore.state().isOpen(); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| fileStore.state().close(); |
| } |
| } |