diff options
Diffstat (limited to 'jimfs/src/main/java/com/google/common/jimfs/JimfsSecureDirectoryStream.java')
-rw-r--r-- | jimfs/src/main/java/com/google/common/jimfs/JimfsSecureDirectoryStream.java | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsSecureDirectoryStream.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsSecureDirectoryStream.java new file mode 100644 index 0000000..e3391b6 --- /dev/null +++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsSecureDirectoryStream.java @@ -0,0 +1,217 @@ +/* + * 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 static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.ImmutableSet; +import java.io.IOException; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.ClosedDirectoryStreamException; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.LinkOption; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.ProviderMismatchException; +import java.nio.file.SecureDirectoryStream; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.util.Iterator; +import java.util.Set; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; + +/** + * Secure directory stream implementation that uses a {@link FileSystemView} with the stream's + * directory as its working directory. + * + * @author Colin Decker + */ +final class JimfsSecureDirectoryStream implements SecureDirectoryStream<Path> { + + private final FileSystemView view; + private final Filter<? super Path> filter; + private final FileSystemState fileSystemState; + + private boolean open = true; + private Iterator<Path> iterator = new DirectoryIterator(); + + public JimfsSecureDirectoryStream( + FileSystemView view, Filter<? super Path> filter, FileSystemState fileSystemState) { + this.view = checkNotNull(view); + this.filter = checkNotNull(filter); + this.fileSystemState = fileSystemState; + fileSystemState.register(this); + } + + private JimfsPath path() { + return view.getWorkingDirectoryPath(); + } + + @Override + public synchronized Iterator<Path> iterator() { + checkOpen(); + Iterator<Path> result = iterator; + checkState(result != null, "iterator() has already been called once"); + iterator = null; + return result; + } + + @Override + public synchronized void close() { + open = false; + fileSystemState.unregister(this); + } + + protected synchronized void checkOpen() { + if (!open) { + throw new ClosedDirectoryStreamException(); + } + } + + private final class DirectoryIterator extends AbstractIterator<Path> { + + @NullableDecl private Iterator<Name> fileNames; + + @Override + protected synchronized Path computeNext() { + checkOpen(); + + try { + if (fileNames == null) { + fileNames = view.snapshotWorkingDirectoryEntries().iterator(); + } + + while (fileNames.hasNext()) { + Name name = fileNames.next(); + Path path = view.getWorkingDirectoryPath().resolve(name); + + if (filter.accept(path)) { + return path; + } + } + + return endOfData(); + } catch (IOException e) { + throw new DirectoryIteratorException(e); + } + } + } + + /** A stream filter that always returns true. */ + public static final Filter<Object> ALWAYS_TRUE_FILTER = + new Filter<Object>() { + @Override + public boolean accept(Object entry) throws IOException { + return true; + } + }; + + @Override + public SecureDirectoryStream<Path> newDirectoryStream(Path path, LinkOption... options) + throws IOException { + checkOpen(); + JimfsPath checkedPath = checkPath(path); + + // safe cast because a file system that supports SecureDirectoryStream always creates + // SecureDirectoryStreams + return (SecureDirectoryStream<Path>) + view.newDirectoryStream( + checkedPath, + ALWAYS_TRUE_FILTER, + Options.getLinkOptions(options), + path().resolve(checkedPath)); + } + + @Override + public SeekableByteChannel newByteChannel( + Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException { + checkOpen(); + JimfsPath checkedPath = checkPath(path); + ImmutableSet<OpenOption> opts = Options.getOptionsForChannel(options); + return new JimfsFileChannel( + view.getOrCreateRegularFile(checkedPath, opts), opts, fileSystemState); + } + + @Override + public void deleteFile(Path path) throws IOException { + checkOpen(); + JimfsPath checkedPath = checkPath(path); + view.deleteFile(checkedPath, FileSystemView.DeleteMode.NON_DIRECTORY_ONLY); + } + + @Override + public void deleteDirectory(Path path) throws IOException { + checkOpen(); + JimfsPath checkedPath = checkPath(path); + view.deleteFile(checkedPath, FileSystemView.DeleteMode.DIRECTORY_ONLY); + } + + @Override + public void move(Path srcPath, SecureDirectoryStream<Path> targetDir, Path targetPath) + throws IOException { + checkOpen(); + JimfsPath checkedSrcPath = checkPath(srcPath); + JimfsPath checkedTargetPath = checkPath(targetPath); + + if (!(targetDir instanceof JimfsSecureDirectoryStream)) { + throw new ProviderMismatchException( + "targetDir isn't a secure directory stream associated with this file system"); + } + + JimfsSecureDirectoryStream checkedTargetDir = (JimfsSecureDirectoryStream) targetDir; + + view.copy( + checkedSrcPath, + checkedTargetDir.view, + checkedTargetPath, + ImmutableSet.<CopyOption>of(), + true); + } + + @Override + public <V extends FileAttributeView> V getFileAttributeView(Class<V> type) { + return getFileAttributeView(path().getFileSystem().getPath("."), type); + } + + @Override + public <V extends FileAttributeView> V getFileAttributeView( + Path path, Class<V> type, LinkOption... options) { + checkOpen(); + final JimfsPath checkedPath = checkPath(path); + final ImmutableSet<LinkOption> optionsSet = Options.getLinkOptions(options); + return view.getFileAttributeView( + new FileLookup() { + @Override + public File lookup() throws IOException { + checkOpen(); // per the spec, must check that the stream is open for each view operation + return view.lookUpWithLock(checkedPath, optionsSet).requireExists(checkedPath).file(); + } + }, + type); + } + + private static JimfsPath checkPath(Path path) { + if (path instanceof JimfsPath) { + return (JimfsPath) path; + } + throw new ProviderMismatchException( + "path " + path + " is not associated with a Jimfs file system"); + } +} |