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