• 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 import static com.google.common.base.Preconditions.checkState;
21 
22 import com.google.common.collect.AbstractIterator;
23 import com.google.common.collect.ImmutableSet;
24 import java.io.IOException;
25 import java.nio.channels.SeekableByteChannel;
26 import java.nio.file.ClosedDirectoryStreamException;
27 import java.nio.file.CopyOption;
28 import java.nio.file.DirectoryIteratorException;
29 import java.nio.file.LinkOption;
30 import java.nio.file.OpenOption;
31 import java.nio.file.Path;
32 import java.nio.file.ProviderMismatchException;
33 import java.nio.file.SecureDirectoryStream;
34 import java.nio.file.attribute.FileAttribute;
35 import java.nio.file.attribute.FileAttributeView;
36 import java.util.Iterator;
37 import java.util.Set;
38 import org.checkerframework.checker.nullness.compatqual.NullableDecl;
39 
40 /**
41  * Secure directory stream implementation that uses a {@link FileSystemView} with the stream's
42  * directory as its working directory.
43  *
44  * @author Colin Decker
45  */
46 final class JimfsSecureDirectoryStream implements SecureDirectoryStream<Path> {
47 
48   private final FileSystemView view;
49   private final Filter<? super Path> filter;
50   private final FileSystemState fileSystemState;
51 
52   private boolean open = true;
53   private Iterator<Path> iterator = new DirectoryIterator();
54 
JimfsSecureDirectoryStream( FileSystemView view, Filter<? super Path> filter, FileSystemState fileSystemState)55   public JimfsSecureDirectoryStream(
56       FileSystemView view, Filter<? super Path> filter, FileSystemState fileSystemState) {
57     this.view = checkNotNull(view);
58     this.filter = checkNotNull(filter);
59     this.fileSystemState = fileSystemState;
60     fileSystemState.register(this);
61   }
62 
path()63   private JimfsPath path() {
64     return view.getWorkingDirectoryPath();
65   }
66 
67   @Override
iterator()68   public synchronized Iterator<Path> iterator() {
69     checkOpen();
70     Iterator<Path> result = iterator;
71     checkState(result != null, "iterator() has already been called once");
72     iterator = null;
73     return result;
74   }
75 
76   @Override
close()77   public synchronized void close() {
78     open = false;
79     fileSystemState.unregister(this);
80   }
81 
checkOpen()82   protected synchronized void checkOpen() {
83     if (!open) {
84       throw new ClosedDirectoryStreamException();
85     }
86   }
87 
88   private final class DirectoryIterator extends AbstractIterator<Path> {
89 
90     @NullableDecl private Iterator<Name> fileNames;
91 
92     @Override
computeNext()93     protected synchronized Path computeNext() {
94       checkOpen();
95 
96       try {
97         if (fileNames == null) {
98           fileNames = view.snapshotWorkingDirectoryEntries().iterator();
99         }
100 
101         while (fileNames.hasNext()) {
102           Name name = fileNames.next();
103           Path path = view.getWorkingDirectoryPath().resolve(name);
104 
105           if (filter.accept(path)) {
106             return path;
107           }
108         }
109 
110         return endOfData();
111       } catch (IOException e) {
112         throw new DirectoryIteratorException(e);
113       }
114     }
115   }
116 
117   /** A stream filter that always returns true. */
118   public static final Filter<Object> ALWAYS_TRUE_FILTER =
119       new Filter<Object>() {
120         @Override
121         public boolean accept(Object entry) throws IOException {
122           return true;
123         }
124       };
125 
126   @Override
newDirectoryStream(Path path, LinkOption... options)127   public SecureDirectoryStream<Path> newDirectoryStream(Path path, LinkOption... options)
128       throws IOException {
129     checkOpen();
130     JimfsPath checkedPath = checkPath(path);
131 
132     // safe cast because a file system that supports SecureDirectoryStream always creates
133     // SecureDirectoryStreams
134     return (SecureDirectoryStream<Path>)
135         view.newDirectoryStream(
136             checkedPath,
137             ALWAYS_TRUE_FILTER,
138             Options.getLinkOptions(options),
139             path().resolve(checkedPath));
140   }
141 
142   @Override
newByteChannel( Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)143   public SeekableByteChannel newByteChannel(
144       Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
145     checkOpen();
146     JimfsPath checkedPath = checkPath(path);
147     ImmutableSet<OpenOption> opts = Options.getOptionsForChannel(options);
148     return new JimfsFileChannel(
149         view.getOrCreateRegularFile(checkedPath, opts), opts, fileSystemState);
150   }
151 
152   @Override
deleteFile(Path path)153   public void deleteFile(Path path) throws IOException {
154     checkOpen();
155     JimfsPath checkedPath = checkPath(path);
156     view.deleteFile(checkedPath, FileSystemView.DeleteMode.NON_DIRECTORY_ONLY);
157   }
158 
159   @Override
deleteDirectory(Path path)160   public void deleteDirectory(Path path) throws IOException {
161     checkOpen();
162     JimfsPath checkedPath = checkPath(path);
163     view.deleteFile(checkedPath, FileSystemView.DeleteMode.DIRECTORY_ONLY);
164   }
165 
166   @Override
move(Path srcPath, SecureDirectoryStream<Path> targetDir, Path targetPath)167   public void move(Path srcPath, SecureDirectoryStream<Path> targetDir, Path targetPath)
168       throws IOException {
169     checkOpen();
170     JimfsPath checkedSrcPath = checkPath(srcPath);
171     JimfsPath checkedTargetPath = checkPath(targetPath);
172 
173     if (!(targetDir instanceof JimfsSecureDirectoryStream)) {
174       throw new ProviderMismatchException(
175           "targetDir isn't a secure directory stream associated with this file system");
176     }
177 
178     JimfsSecureDirectoryStream checkedTargetDir = (JimfsSecureDirectoryStream) targetDir;
179 
180     view.copy(
181         checkedSrcPath,
182         checkedTargetDir.view,
183         checkedTargetPath,
184         ImmutableSet.<CopyOption>of(),
185         true);
186   }
187 
188   @Override
getFileAttributeView(Class<V> type)189   public <V extends FileAttributeView> V getFileAttributeView(Class<V> type) {
190     return getFileAttributeView(path().getFileSystem().getPath("."), type);
191   }
192 
193   @Override
getFileAttributeView( Path path, Class<V> type, LinkOption... options)194   public <V extends FileAttributeView> V getFileAttributeView(
195       Path path, Class<V> type, LinkOption... options) {
196     checkOpen();
197     final JimfsPath checkedPath = checkPath(path);
198     final ImmutableSet<LinkOption> optionsSet = Options.getLinkOptions(options);
199     return view.getFileAttributeView(
200         new FileLookup() {
201           @Override
202           public File lookup() throws IOException {
203             checkOpen(); // per the spec, must check that the stream is open for each view operation
204             return view.lookUpWithLock(checkedPath, optionsSet).requireExists(checkedPath).file();
205           }
206         },
207         type);
208   }
209 
210   private static JimfsPath checkPath(Path path) {
211     if (path instanceof JimfsPath) {
212       return (JimfsPath) path;
213     }
214     throw new ProviderMismatchException(
215         "path " + path + " is not associated with a Jimfs file system");
216   }
217 }
218