• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.jimfs;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.VisibleForTesting;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.ImmutableSortedSet;
24 import com.google.common.util.concurrent.ThreadFactoryBuilder;
25 import java.io.Closeable;
26 import java.io.IOException;
27 import java.net.URI;
28 import java.nio.file.FileStore;
29 import java.nio.file.FileSystem;
30 import java.nio.file.Path;
31 import java.nio.file.PathMatcher;
32 import java.nio.file.WatchService;
33 import java.nio.file.attribute.UserPrincipalLookupService;
34 import java.util.concurrent.ExecutorService;
35 import java.util.concurrent.Executors;
36 import org.checkerframework.checker.nullness.compatqual.NullableDecl;
37 
38 /**
39  * {@link FileSystem} implementation for Jimfs. Most behavior for the file system is implemented by
40  * its {@linkplain #getDefaultView() default file system view}.
41  *
42  * <h3>Overview of file system design</h3>
43  *
44  * {@link com.google.common.jimfs.JimfsFileSystem JimfsFileSystem} instances are created by {@link
45  * com.google.common.jimfs.JimfsFileSystems JimfsFileSystems} using a user-provided {@link
46  * com.google.common.jimfs.Configuration Configuration}. The configuration is used to create the
47  * various classes that implement the file system with the correct settings and to create the file
48  * system root directories and working directory. The file system is then used to create the {@code
49  * Path} objects that all file system operations use.
50  *
51  * <p>Once created, the primary entry points to the file system are {@link
52  * com.google.common.jimfs.JimfsFileSystemProvider JimfsFileSystemProvider}, which handles calls to
53  * methods in {@link java.nio.file.Files}, and {@link
54  * com.google.common.jimfs.JimfsSecureDirectoryStream JimfsSecureDirectoryStream}, which provides
55  * methods that are similar to those of the file system provider but which treat relative paths as
56  * relative to the stream's directory rather than the file system's working directory.
57  *
58  * <p>The implementation of the methods on both of those classes is handled by the {@link
59  * com.google.common.jimfs.FileSystemView FileSystemView} class, which acts as a view of the file
60  * system with a specific working directory. The file system provider uses the file system's default
61  * view, while each secure directory stream uses a view specific to that stream.
62  *
63  * <p>File system views make use of the file system's singleton {@link
64  * com.google.common.jimfs.JimfsFileStore JimfsFileStore} which handles file creation, storage and
65  * attributes. The file store delegates to several other classes to handle each of these:
66  *
67  * <ul>
68  *   <li>{@link com.google.common.jimfs.FileFactory FileFactory} handles creation of new file
69  *       objects.
70  *   <li>{@link com.google.common.jimfs.HeapDisk HeapDisk} handles allocation of blocks to {@link
71  *       RegularFile RegularFile} instances.
72  *   <li>{@link com.google.common.jimfs.FileTree FileTree} stores the root of the file hierarchy and
73  *       handles file lookup.
74  *   <li>{@link com.google.common.jimfs.AttributeService AttributeService} handles file attributes,
75  *       using a set of {@link com.google.common.jimfs.AttributeProvider AttributeProvider}
76  *       implementations to handle each supported file attribute view.
77  * </ul>
78  *
79  * <h3>Paths</h3>
80  *
81  * The implementation of {@link java.nio.file.Path} for the file system is {@link
82  * com.google.common.jimfs.JimfsPath JimfsPath}. Paths are created by a {@link
83  * com.google.common.jimfs.PathService PathService} with help from the file system's configured
84  * {@link com.google.common.jimfs.PathType PathType}.
85  *
86  * <p>Paths are made up of {@link com.google.common.jimfs.Name Name} objects, which also serve as
87  * the file names in directories. A name has two forms:
88  *
89  * <ul>
90  *   <li>The <b>display form</b> is used in {@code Path} for {@code toString()}. It is also used for
91  *       determining the equality and sort order of {@code Path} objects for most file systems.
92  *   <li>The <b>canonical form</b> is used for equality of two {@code Name} objects. This affects
93  *       the notion of name equality in the file system itself for file lookup. A file system may be
94  *       configured to use the canonical form of the name for path equality (a Windows-like file
95  *       system configuration does this, as the real Windows file system implementation uses
96  *       case-insensitive equality for its path objects.
97  * </ul>
98  *
99  * <p>The canonical form of a name is created by applying a series of {@linkplain PathNormalization
100  * normalizations} to the original string. These normalization may be either a Unicode normalization
101  * (e.g. NFD) or case folding normalization for case-insensitivity. Normalizations may also be
102  * applied to the display form of a name, but this is currently only done for a Mac OS X type
103  * configuration.
104  *
105  * <h3>Files</h3>
106  *
107  * All files in the file system are an instance of {@link com.google.common.jimfs.File File}. A file
108  * object contains both the file's attributes and content.
109  *
110  * <p>There are three types of files:
111  *
112  * <ul>
113  *   <li>{@link Directory Directory} - contains a table linking file names to {@linkplain
114  *       com.google.common.jimfs.DirectoryEntry directory entries}.
115  *   <li>{@link RegularFile RegularFile} - an in-memory store for raw bytes.
116  *   <li>{@link com.google.common.jimfs.SymbolicLink SymbolicLink} - contains a path.
117  * </ul>
118  *
119  * <p>{@link com.google.common.jimfs.JimfsFileChannel JimfsFileChannel}, {@link
120  * com.google.common.jimfs.JimfsInputStream JimfsInputStream} and {@link
121  * com.google.common.jimfs.JimfsOutputStream JimfsOutputStream} implement the standard
122  * channel/stream APIs for regular files.
123  *
124  * <p>{@link com.google.common.jimfs.JimfsSecureDirectoryStream JimfsSecureDirectoryStream} handles
125  * reading the entries of a directory. The secure directory stream additionally contains a {@code
126  * FileSystemView} with its directory as the working directory, allowing for operations relative to
127  * the actual directory file rather than just the path to the file. This allows the operations to
128  * continue to work as expected even if the directory is moved.
129  *
130  * <p>A directory can be watched for changes using the {@link java.nio.file.WatchService}
131  * implementation, {@link com.google.common.jimfs.PollingWatchService PollingWatchService}.
132  *
133  * <h3>Regular files</h3>
134  *
135  * {@link RegularFile RegularFile} makes use of a singleton {@link com.google.common.jimfs.HeapDisk
136  * HeapDisk}. A disk is a resizable factory and cache for fixed size blocks of memory. These blocks
137  * are allocated to files as needed and returned to the disk when a file is deleted or truncated.
138  * When cached free blocks are available, those blocks are allocated to files first. If more blocks
139  * are needed, they are created.
140  *
141  * <h3>Linking</h3>
142  *
143  * When a file is mapped to a file name in a directory table, it is <i>linked</i>. Each type of file
144  * has different rules governing how it is linked.
145  *
146  * <ul>
147  *   <li>Directory - A directory has two or more links to it. The first is the link from its parent
148  *       directory to it. This link is the name of the directory. The second is the <i>self</i> link
149  *       (".") which links the directory to itself. The directory may also have any number of
150  *       additional <i>parent</i> links ("..") from child directories back to it.
151  *   <li>Regular file - A regular file has one link from its parent directory by default. However,
152  *       regular files are also allowed to have any number of additional user-created hard links,
153  *       from the same directory with different names and/or from other directories with any names.
154  *   <li>Symbolic link - A symbolic link can only have one link, from its parent directory.
155  * </ul>
156  *
157  * <h3>Thread safety</h3>
158  *
159  * All file system operations should be safe in a multithreaded environment. The file hierarchy
160  * itself is protected by a file system level read-write lock. This ensures safety of all
161  * modifications to directory tables as well as atomicity of operations like file moves. Regular
162  * files are each protected by a read-write lock which is obtained for each read or write operation.
163  * File attributes are protected by synchronization on the file object itself.
164  *
165  * @author Colin Decker
166  */
167 final class JimfsFileSystem extends FileSystem {
168 
169   private final JimfsFileSystemProvider provider;
170   private final URI uri;
171 
172   private final JimfsFileStore fileStore;
173   private final PathService pathService;
174 
175   private final UserPrincipalLookupService userLookupService = new UserLookupService(true);
176 
177   private final FileSystemView defaultView;
178 
179   private final WatchServiceConfiguration watchServiceConfig;
180 
JimfsFileSystem( JimfsFileSystemProvider provider, URI uri, JimfsFileStore fileStore, PathService pathService, FileSystemView defaultView, WatchServiceConfiguration watchServiceConfig)181   JimfsFileSystem(
182       JimfsFileSystemProvider provider,
183       URI uri,
184       JimfsFileStore fileStore,
185       PathService pathService,
186       FileSystemView defaultView,
187       WatchServiceConfiguration watchServiceConfig) {
188     this.provider = checkNotNull(provider);
189     this.uri = checkNotNull(uri);
190     this.fileStore = checkNotNull(fileStore);
191     this.pathService = checkNotNull(pathService);
192     this.defaultView = checkNotNull(defaultView);
193     this.watchServiceConfig = checkNotNull(watchServiceConfig);
194   }
195 
196   @Override
provider()197   public JimfsFileSystemProvider provider() {
198     return provider;
199   }
200 
201   /** Returns the URI for this file system. */
getUri()202   public URI getUri() {
203     return uri;
204   }
205 
206   /** Returns the default view for this file system. */
getDefaultView()207   public FileSystemView getDefaultView() {
208     return defaultView;
209   }
210 
211   @Override
getSeparator()212   public String getSeparator() {
213     return pathService.getSeparator();
214   }
215 
216   @SuppressWarnings("unchecked") // safe cast of immutable set
217   @Override
getRootDirectories()218   public ImmutableSortedSet<Path> getRootDirectories() {
219     ImmutableSortedSet.Builder<JimfsPath> builder = ImmutableSortedSet.orderedBy(pathService);
220     for (Name name : fileStore.getRootDirectoryNames()) {
221       builder.add(pathService.createRoot(name));
222     }
223     return (ImmutableSortedSet<Path>) (ImmutableSortedSet<?>) builder.build();
224   }
225 
226   /** Returns the working directory path for this file system. */
getWorkingDirectory()227   public JimfsPath getWorkingDirectory() {
228     return defaultView.getWorkingDirectoryPath();
229   }
230 
231   /** Returns the path service for this file system. */
232   @VisibleForTesting
getPathService()233   PathService getPathService() {
234     return pathService;
235   }
236 
237   /** Returns the file store for this file system. */
getFileStore()238   public JimfsFileStore getFileStore() {
239     return fileStore;
240   }
241 
242   @Override
getFileStores()243   public ImmutableSet<FileStore> getFileStores() {
244     fileStore.state().checkOpen();
245     return ImmutableSet.<FileStore>of(fileStore);
246   }
247 
248   @Override
supportedFileAttributeViews()249   public ImmutableSet<String> supportedFileAttributeViews() {
250     return fileStore.supportedFileAttributeViews();
251   }
252 
253   @Override
getPath(String first, String... more)254   public JimfsPath getPath(String first, String... more) {
255     fileStore.state().checkOpen();
256     return pathService.parsePath(first, more);
257   }
258 
259   /** Gets the URI of the given path in this file system. */
toUri(JimfsPath path)260   public URI toUri(JimfsPath path) {
261     fileStore.state().checkOpen();
262     return pathService.toUri(uri, path.toAbsolutePath());
263   }
264 
265   /** Converts the given URI into a path in this file system. */
toPath(URI uri)266   public JimfsPath toPath(URI uri) {
267     fileStore.state().checkOpen();
268     return pathService.fromUri(uri);
269   }
270 
271   @Override
getPathMatcher(String syntaxAndPattern)272   public PathMatcher getPathMatcher(String syntaxAndPattern) {
273     fileStore.state().checkOpen();
274     return pathService.createPathMatcher(syntaxAndPattern);
275   }
276 
277   @Override
getUserPrincipalLookupService()278   public UserPrincipalLookupService getUserPrincipalLookupService() {
279     fileStore.state().checkOpen();
280     return userLookupService;
281   }
282 
283   @Override
newWatchService()284   public WatchService newWatchService() throws IOException {
285     return watchServiceConfig.newWatchService(defaultView, pathService);
286   }
287 
288   @NullableDecl private ExecutorService defaultThreadPool;
289 
290   /**
291    * Returns a default thread pool to use for asynchronous file channels when users do not provide
292    * an executor themselves. (This is required by the spec of newAsynchronousFileChannel in
293    * FileSystemProvider.)
294    */
getDefaultThreadPool()295   public synchronized ExecutorService getDefaultThreadPool() {
296     if (defaultThreadPool == null) {
297       defaultThreadPool =
298           Executors.newCachedThreadPool(
299               new ThreadFactoryBuilder()
300                   .setDaemon(true)
301                   .setNameFormat("JimfsFileSystem-" + uri.getHost() + "-defaultThreadPool-%s")
302                   .build());
303 
304       // ensure thread pool is closed when file system is closed
305       fileStore
306           .state()
307           .register(
308               new Closeable() {
309                 @Override
310                 public void close() {
311                   defaultThreadPool.shutdown();
312                 }
313               });
314     }
315     return defaultThreadPool;
316   }
317 
318   /**
319    * Returns {@code false}; currently, cannot create a read-only file system.
320    *
321    * @return {@code false}, always
322    */
323   @Override
isReadOnly()324   public boolean isReadOnly() {
325     return false;
326   }
327 
328   @Override
isOpen()329   public boolean isOpen() {
330     return fileStore.state().isOpen();
331   }
332 
333   @Override
close()334   public void close() throws IOException {
335     fileStore.state().close();
336   }
337 }
338