| /* |
| * 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.jimfs.internal; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.jimfs.internal.LinkHandling.NOFOLLOW_LINKS; |
| |
| import com.google.common.base.Throwables; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import com.google.jimfs.JimfsConfiguration; |
| import com.google.jimfs.internal.file.DirectoryTable; |
| import com.google.jimfs.internal.file.File; |
| import com.google.jimfs.internal.path.JimfsPath; |
| import com.google.jimfs.internal.path.Name; |
| import com.google.jimfs.internal.path.PathMatchers; |
| import com.google.jimfs.internal.watch.PollingWatchService; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.nio.file.FileStore; |
| import java.nio.file.FileSystem; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.PathMatcher; |
| import java.nio.file.WatchService; |
| import java.nio.file.attribute.UserPrincipalLookupService; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.locks.ReadWriteLock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| /** |
| * @author Colin Decker |
| */ |
| public final class JimfsFileSystem extends FileSystem { |
| |
| /** Set of closeables that need to be closed when this file system is closed. */ |
| private final Set<Closeable> openCloseables = Sets.newConcurrentHashSet(); |
| |
| private final JimfsFileSystemProvider provider; |
| private final JimfsConfiguration configuration; |
| private final JimfsFileStore store; |
| |
| private final ImmutableSet<JimfsPath> rootDirPaths; |
| private final JimfsPath workingDirPath; |
| |
| private final ReadWriteLock lock = new ReentrantReadWriteLock(); |
| |
| private final FileTree superRootTree; |
| private final FileTree workingDirTree; |
| |
| public JimfsFileSystem(JimfsFileSystemProvider provider, JimfsConfiguration config) { |
| this.provider = checkNotNull(provider); |
| this.configuration = checkNotNull(config); |
| this.store = new JimfsFileStore("jimfs", config.getAttributeProviders()); |
| |
| Set<JimfsPath> rootPaths = new HashSet<>(); |
| for (String root : config.getRoots()) { |
| rootPaths.add(JimfsPath.root(this, config.createName(root, true))); |
| } |
| this.rootDirPaths = ImmutableSet.copyOf(rootPaths); |
| this.workingDirPath = getPath(config.getWorkingDirectory()); |
| |
| if (rootDirPaths.isEmpty()) { |
| this.superRootTree = null; |
| this.workingDirTree = null; |
| } else { |
| File superRoot = store.createDirectory(); |
| DirectoryTable superRootTable = superRoot.content(); |
| for (JimfsPath path : rootDirPaths) { |
| File dir = store.createDirectory(); |
| superRootTable.link(path.getRootName(), dir); |
| |
| DirectoryTable dirTable = dir.content(); |
| dirTable.linkSelf(dir); |
| dirTable.linkParent(dir); |
| } |
| |
| this.superRootTree = new FileTree(superRoot, JimfsPath.empty(this), null, lock(), |
| store); |
| this.workingDirTree = new FileTree(createWorkingDirectory(workingDirPath), workingDirPath, |
| superRootTree, lock(), store); |
| } |
| } |
| |
| private File createWorkingDirectory(JimfsPath workingDir) { |
| try { |
| Files.createDirectories(workingDir); |
| return superRootTree.lookupFile(workingDir, NOFOLLOW_LINKS); |
| } catch (IOException e) { |
| throw new RuntimeException("failed to create working dir", e); |
| } |
| } |
| |
| @Override |
| public JimfsFileSystemProvider provider() { |
| return provider; |
| } |
| |
| /** |
| * Returns the configuration for this file system. |
| */ |
| public JimfsConfiguration configuration() { |
| return configuration; |
| } |
| |
| @Override |
| public String getSeparator() { |
| return configuration.getSeparator(); |
| } |
| |
| @SuppressWarnings("unchecked") // safe because set is immutable |
| @Override |
| public ImmutableSet<Path> getRootDirectories() { |
| return (ImmutableSet<Path>) (ImmutableSet) rootDirPaths; |
| } |
| |
| /** |
| * Returns the working directory path for this file system. |
| */ |
| public JimfsPath getWorkingDirectory() { |
| return workingDirPath; |
| } |
| |
| @Override |
| public ImmutableSet<FileStore> getFileStores() { |
| return ImmutableSet.<FileStore>of(store); |
| } |
| |
| @Override |
| public ImmutableSet<String> supportedFileAttributeViews() { |
| return store.supportedFileAttributeViews(); |
| } |
| |
| @Override |
| public JimfsPath getPath(String first, String... more) { |
| List<String> parts = new ArrayList<>(); |
| for (String s : Lists.asList(first, more)) { |
| if (!s.isEmpty()) { |
| parts.add(s); |
| } |
| } |
| return configuration.parsePath(this, parts); |
| } |
| |
| /** |
| * Returns the {@link Name} representation of the given string for this file system. |
| */ |
| public Name name(String name) { |
| return configuration.createName(name, false); |
| } |
| |
| @Override |
| public PathMatcher getPathMatcher(String syntaxAndPattern) { |
| return PathMatchers.getPathMatcher(syntaxAndPattern, configuration.getRecognizedSeparators()); |
| } |
| |
| @Override |
| public UserPrincipalLookupService getUserPrincipalLookupService() { |
| return null; |
| } |
| |
| @Override |
| public WatchService newWatchService() throws IOException { |
| return opened(new PollingWatchService(this)); |
| } |
| |
| /** |
| * Returns {@code false}; currently, cannot create a read-only file system. |
| * |
| * @return {@code false}, always |
| */ |
| @Override |
| public boolean isReadOnly() { |
| return false; |
| } |
| |
| /** |
| * Returns the read/write lock for this file system. |
| */ |
| public ReadWriteLock lock() { |
| return lock; |
| } |
| |
| /** |
| * Returns the file tree to use for the given path. If the path is absolute, the super root is |
| * returned. Otherwise, the working directory is returned. |
| */ |
| public FileTree getFileTree(JimfsPath path) { |
| return path.isAbsolute() ? superRootTree : workingDirTree; |
| } |
| |
| @Override |
| public boolean isOpen() { |
| return true; |
| } |
| |
| /** |
| * Called when a closeable associated with this file system is opened. Returns the given |
| * closeable. |
| */ |
| public <C extends Closeable> C opened(C closeable) { |
| openCloseables.add(closeable); |
| return closeable; |
| } |
| |
| /** |
| * Called when an opened closeable such as a watch service is closed. |
| */ |
| public void closed(Closeable closeable) { |
| openCloseables.remove(closeable); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| Throwable thrown = null; |
| for (Closeable closeable : openCloseables) { |
| try { |
| closeable.close(); |
| } catch (Throwable e) { |
| if (thrown == null) { |
| thrown = e; |
| } else { |
| thrown.addSuppressed(e); |
| } |
| } |
| } |
| |
| Throwables.propagateIfPossible(thrown, IOException.class); |
| } |
| } |