blob: a929f1435d524aa5a46395b18f8efec048b267e7 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.vfs;
16
tomlua155b532017-11-08 20:12:47 +010017import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.devtools.build.lib.concurrent.ThreadSafety;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019import java.io.IOException;
20import java.io.InputStream;
21import java.io.OutputStream;
tomlua729b9b2018-02-08 15:32:00 -080022import java.util.ArrayList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import java.util.Collection;
tomlua729b9b2018-02-08 15:32:00 -080024import java.util.Comparator;
25import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import java.util.Map;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027import javax.annotation.Nullable;
28
29/**
30 * Presents a unified view of multiple virtual {@link FileSystem} instances, to which requests are
ccalvarinfb3293c2017-09-26 11:13:33 -040031 * delegated based on a {@link PathFragment} prefix mapping. If multiple prefixes apply to a given
32 * path, the *longest* (i.e. most specific) match is used. The order in which the delegates are
33 * specified does not influence the mapping.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034 *
ccalvarinfb3293c2017-09-26 11:13:33 -040035 * <p>Paths are preserved absolutely, contrary to how "mount" works, e.g.: /foo/bar maps to /foo/bar
36 * on the delegate, even if it is mounted at /foo.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037 *
ccalvarinfb3293c2017-09-26 11:13:33 -040038 * <p>For example: "/in" maps to InFileSystem, "/" maps to OtherFileSystem. Reading from
39 * "/in/base/BUILD" through the UnionFileSystem will delegate the read operation to InFileSystem,
40 * which will read "/in/base/BUILD" relative to its root. ("mount" behavior would remap it to
41 * "/base/BUILD" on the delegate).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042 *
ccalvarinfb3293c2017-09-26 11:13:33 -040043 * <p>Intra-filesystem symbolic links are resolved to their ultimate targets. Cross-filesystem links
44 * are not currently supported.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045 */
46@ThreadSafety.ThreadSafe
tomlua729b9b2018-02-08 15:32:00 -080047public final class UnionFileSystem extends FileSystem {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048
tomlua729b9b2018-02-08 15:32:00 -080049 private static class FileSystemAndPrefix {
50 final PathFragment prefix;
51 final FileSystem fileSystem;
52
53 public FileSystemAndPrefix(PathFragment prefix, FileSystem fileSystem) {
54 this.prefix = prefix;
55 this.fileSystem = fileSystem;
56 }
57 }
58
59 // List of file systems and their mappings, sorted by prefix length descending.
60 private final List<FileSystemAndPrefix> fileSystems;
61 private final FileSystem rootFileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010062
Yun Peng352f7e72016-05-09 11:08:25 +000063 // True if the file path is case-sensitive on all the FileSystem
64 // or False if they are all case-insensitive, otherwise error.
65 private final boolean isCaseSensitive;
66
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067 /**
ccalvarinfb3293c2017-09-26 11:13:33 -040068 * Creates a new modifiable UnionFileSystem with prefix mappings specified by a map.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069 *
70 * @param prefixMapping map of path prefixes to {@link FileSystem}s
tomlu121c6b12017-10-20 21:16:57 +020071 * @param rootFileSystem root for default requests; i.e. mapping of "/"
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072 */
aehligc801c392017-12-19 07:12:25 -080073 public UnionFileSystem(Map<PathFragment, FileSystem> prefixMapping, FileSystem rootFileSystem) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010074 super();
75 Preconditions.checkNotNull(prefixMapping);
76 Preconditions.checkNotNull(rootFileSystem);
77 Preconditions.checkArgument(rootFileSystem != this, "Circular root filesystem.");
78 Preconditions.checkArgument(
tomlua729b9b2018-02-08 15:32:00 -080079 prefixMapping.keySet().stream().noneMatch(p -> p.getPathString().equals("/")),
ccalvarinfb3293c2017-09-26 11:13:33 -040080 "Attempted to specify an explicit root prefix mapping; "
81 + "please use the rootFileSystem argument instead.");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010082
tomlua729b9b2018-02-08 15:32:00 -080083 this.fileSystems = new ArrayList<>();
84 this.rootFileSystem = rootFileSystem;
Yun Peng352f7e72016-05-09 11:08:25 +000085 this.isCaseSensitive = rootFileSystem.isFilePathCaseSensitive();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086
aehligc801c392017-12-19 07:12:25 -080087 for (Map.Entry<PathFragment, FileSystem> prefix : prefixMapping.entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010088 FileSystem delegate = prefix.getValue();
Yun Peng352f7e72016-05-09 11:08:25 +000089 Preconditions.checkArgument(
90 delegate.isFilePathCaseSensitive() == this.isCaseSensitive,
91 "The case sensitiveness of FileSystem are different in UnionFileSystem");
aehligc801c392017-12-19 07:12:25 -080092 PathFragment prefixPath = prefix.getKey();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010093
94 // Extra slash prevents within-directory mappings, which Path can't handle.
tomlua729b9b2018-02-08 15:32:00 -080095 fileSystems.add(new FileSystemAndPrefix(prefixPath, delegate));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096 }
tomlua729b9b2018-02-08 15:32:00 -080097 // Order by length descending. This ensures that more specific mapping takes precedence
98 // when we try to find the file system of a given path.
99 Comparator<FileSystemAndPrefix> comparator =
100 Comparator.comparing(f -> f.prefix.getPathString().length());
101 fileSystems.sort(comparator.reversed());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102 }
103
104 /**
ccalvarinfb3293c2017-09-26 11:13:33 -0400105 * Retrieves the filesystem delegate of a path mapping. Does not follow symlinks (but you can call
106 * on a path preprocessed with {@link #resolveSymbolicLinks} to support this use case).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100107 *
108 * @param path the {@link Path} to map to a filesystem
109 * @throws IllegalArgumentException if no delegate exists for the path
110 */
tomlua729b9b2018-02-08 15:32:00 -0800111 FileSystem getDelegate(Path path) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100112 Preconditions.checkNotNull(path);
tomlua729b9b2018-02-08 15:32:00 -0800113 FileSystem delegate = null;
114 // Linearly iterate over each mapped file system and find the one that handles this path.
115 // For small number of mappings, this will be more efficient than using a trie
116 for (FileSystemAndPrefix fileSystemAndPrefix : this.fileSystems) {
117 if (path.startsWith(fileSystemAndPrefix.prefix)) {
118 delegate = fileSystemAndPrefix.fileSystem;
119 break;
120 }
121 }
122 return delegate != null ? delegate : rootFileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100123 }
124
125 // Associates the path with the root of the given delegate filesystem.
126 // Necessary to avoid null pointer problems inside of the delegates.
tomlua729b9b2018-02-08 15:32:00 -0800127 Path adjustPath(Path path, FileSystem delegate) {
128 return delegate.getPath(path.getPathString());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100129 }
130
131 /**
ccalvarinfb3293c2017-09-26 11:13:33 -0400132 * Follow a symbolic link once using the appropriate delegate filesystem, also resolving parent
133 * directory symlinks.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100134 *
135 * @param path {@link Path} to the symbolic link
136 */
137 @Override
aehligc801c392017-12-19 07:12:25 -0800138 protected PathFragment readSymbolicLink(Path path) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139 Preconditions.checkNotNull(path);
140 FileSystem delegate = getDelegate(path);
141 return delegate.readSymbolicLink(adjustPath(path, delegate));
142 }
143
144 @Override
aehligc801c392017-12-19 07:12:25 -0800145 protected PathFragment resolveOneLink(Path path) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100146 Preconditions.checkNotNull(path);
147 FileSystem delegate = getDelegate(path);
148 return delegate.resolveOneLink(adjustPath(path, delegate));
149 }
150
aehligc801c392017-12-19 07:12:25 -0800151 private void checkModifiable(Path path) {
tomlu39fab102017-10-19 21:35:06 +0200152 if (!supportsModifications(path)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100153 throw new UnsupportedOperationException(
ccalvarinfb3293c2017-09-26 11:13:33 -0400154 String.format("Modifications to this %s are disabled.", getClass().getSimpleName()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100155 }
156 }
157
158 @Override
aehligc801c392017-12-19 07:12:25 -0800159 public boolean supportsModifications(Path path) {
tomlu121c6b12017-10-20 21:16:57 +0200160 FileSystem delegate = getDelegate(path);
161 path = adjustPath(path, delegate);
162 return delegate.supportsModifications(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100163 }
164
165 @Override
aehligc801c392017-12-19 07:12:25 -0800166 public boolean supportsSymbolicLinksNatively(Path path) {
tomlu121c6b12017-10-20 21:16:57 +0200167 FileSystem delegate = getDelegate(path);
168 path = adjustPath(path, delegate);
169 return delegate.supportsSymbolicLinksNatively(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100170 }
171
172 @Override
aehligc801c392017-12-19 07:12:25 -0800173 public boolean supportsHardLinksNatively(Path path) {
tomlu121c6b12017-10-20 21:16:57 +0200174 FileSystem delegate = getDelegate(path);
175 path = adjustPath(path, delegate);
176 return delegate.supportsHardLinksNatively(path);
Googlere1cd9502016-09-07 14:33:29 +0000177 }
178
179 @Override
Yun Peng352f7e72016-05-09 11:08:25 +0000180 public boolean isFilePathCaseSensitive() {
tomlu121c6b12017-10-20 21:16:57 +0200181 return isCaseSensitive;
Yun Peng352f7e72016-05-09 11:08:25 +0000182 }
183
184 @Override
aehligc801c392017-12-19 07:12:25 -0800185 public String getFileSystemType(Path path) {
tomlu33700d62017-10-20 02:26:00 +0200186 try {
187 path = internalResolveSymlink(path);
188 } catch (IOException e) {
189 return "unknown";
190 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191 FileSystem delegate = getDelegate(path);
192 return delegate.getFileSystemType(path);
193 }
194
195 @Override
aehligc801c392017-12-19 07:12:25 -0800196 protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200197 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 FileSystem delegate = getDelegate(path);
olaolabfd1d332017-06-19 16:55:24 -0400199 return delegate.getDigest(adjustPath(path, delegate), hashFunction);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200 }
201
202 @Override
aehligc801c392017-12-19 07:12:25 -0800203 public boolean createDirectory(Path path) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200204 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100205 // When creating the exact directory that is mapped,
206 // create it on both the parent's delegate and the path's delegate.
207 // This is necessary both for the parent to see the directory and for the
208 // delegate to use it.
209 // This is present to address this problematic case:
210 // / -> RootFs
211 // /foo -> FooFs
212 // mkdir /foo
213 // ls / ("foo" would be missing if not created on the parent)
214 // ls /foo (would fail if foo weren't also present on the child)
215 FileSystem delegate = getDelegate(path);
aehligc801c392017-12-19 07:12:25 -0800216 Path parent = path.getParentDirectory();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100217 if (parent != null) {
tomlu33700d62017-10-20 02:26:00 +0200218 parent = internalResolveSymlink(parent);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100219 FileSystem parentDelegate = getDelegate(parent);
220 if (parentDelegate != delegate) {
221 // There's a possibility it already exists on the parent, so don't die
222 // if the directory can't be created there.
223 parentDelegate.createDirectory(adjustPath(path, parentDelegate));
224 }
225 }
226 return delegate.createDirectory(adjustPath(path, delegate));
227 }
228
229 @Override
tomludecca2b2017-12-21 08:59:51 -0800230 public void createDirectoryAndParents(Path path) throws IOException {
231 checkModifiable(path);
232 FileSystem delegate = getDelegate(path);
tomlu7f17d082018-01-09 13:46:29 -0800233 delegate.createDirectoryAndParents(adjustPath(path, delegate));
tomludecca2b2017-12-21 08:59:51 -0800234 }
235
236 @Override
aehligc801c392017-12-19 07:12:25 -0800237 protected long getFileSize(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200238 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100239 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200240 return delegate.getFileSize(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100241 }
242
243 @Override
aehligc801c392017-12-19 07:12:25 -0800244 public boolean delete(Path path) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200245 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100246 FileSystem delegate = getDelegate(path);
247 return delegate.delete(adjustPath(path, delegate));
248 }
249
250 @Override
aehligc801c392017-12-19 07:12:25 -0800251 protected long getLastModifiedTime(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200252 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100253 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200254 return delegate.getLastModifiedTime(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100255 }
256
257 @Override
aehligc801c392017-12-19 07:12:25 -0800258 public void setLastModifiedTime(Path path, long newTime) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200259 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200260 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100261 FileSystem delegate = getDelegate(path);
262 delegate.setLastModifiedTime(adjustPath(path, delegate), newTime);
263 }
264
265 @Override
aehligc801c392017-12-19 07:12:25 -0800266 protected boolean isSymbolicLink(Path path) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100267 FileSystem delegate = getDelegate(path);
268 path = adjustPath(path, delegate);
269 return delegate.isSymbolicLink(path);
270 }
271
272 @Override
aehligc801c392017-12-19 07:12:25 -0800273 protected boolean isDirectory(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200274 try {
275 path = followSymlinks ? internalResolveSymlink(path) : path;
276 } catch (IOException e) {
277 return false;
278 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100279 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200280 return delegate.isDirectory(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100281 }
282
283 @Override
aehligc801c392017-12-19 07:12:25 -0800284 protected boolean isFile(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200285 try {
286 path = followSymlinks ? internalResolveSymlink(path) : path;
287 } catch (IOException e) {
288 return false;
289 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100290 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200291 return delegate.isFile(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100292 }
293
294 @Override
aehligc801c392017-12-19 07:12:25 -0800295 protected boolean isSpecialFile(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200296 try {
297 path = followSymlinks ? internalResolveSymlink(path) : path;
298 } catch (IOException e) {
299 return false;
300 }
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000301 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200302 return delegate.isSpecialFile(adjustPath(path, delegate), false);
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000303 }
304
305 @Override
aehligc801c392017-12-19 07:12:25 -0800306 protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200307 checkModifiable(linkPath);
308 if (!supportsSymbolicLinksNatively(linkPath)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100309 throw new UnsupportedOperationException(
310 "Attempted to create a symlink, but symlink support is disabled.");
311 }
312
313 FileSystem delegate = getDelegate(linkPath);
314 delegate.createSymbolicLink(adjustPath(linkPath, delegate), targetFragment);
315 }
316
317 @Override
aehligc801c392017-12-19 07:12:25 -0800318 protected boolean exists(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200319 try {
320 path = followSymlinks ? internalResolveSymlink(path) : path;
321 } catch (IOException e) {
322 return false;
323 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100324 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200325 return delegate.exists(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100326 }
327
328 @Override
aehligc801c392017-12-19 07:12:25 -0800329 protected FileStatus stat(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200330 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100331 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200332 return delegate.stat(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100333 }
334
335 // Needs to be overridden for the delegation logic, because the
336 // UnixFileSystem implements statNullable and stat as separate codepaths.
337 // More generally, we wish to delegate all filesystem operations.
338 @Override
aehligc801c392017-12-19 07:12:25 -0800339 protected FileStatus statNullable(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200340 try {
341 path = followSymlinks ? internalResolveSymlink(path) : path;
342 } catch (IOException e) {
343 return null;
344 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100345 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200346 return delegate.statNullable(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100347 }
348
349 @Override
350 @Nullable
aehligc801c392017-12-19 07:12:25 -0800351 protected FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200352 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100353 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200354 return delegate.statIfFound(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100355 }
356
357 /**
ccalvarinfb3293c2017-09-26 11:13:33 -0400358 * Retrieves the directory entries for the specified path under the assumption that {@code
359 * resolvedPath} is the resolved path of {@code path} in one of the underlying file systems.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100360 *
361 * @param path the {@link Path} whose children are to be retrieved
362 */
363 @Override
aehligc801c392017-12-19 07:12:25 -0800364 protected Collection<String> getDirectoryEntries(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200365 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100366 FileSystem delegate = getDelegate(path);
aehligc801c392017-12-19 07:12:25 -0800367 Path resolvedPath = adjustPath(path, delegate);
tomlua729b9b2018-02-08 15:32:00 -0800368 return delegate.getDirectoryEntries(resolvedPath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100369 }
370
371 // No need for the more complex logic of getDirectoryEntries; it calls it implicitly.
372 @Override
aehligc801c392017-12-19 07:12:25 -0800373 protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200374 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100375 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200376 return delegate.readdir(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100377 }
378
379 @Override
aehligc801c392017-12-19 07:12:25 -0800380 protected boolean isReadable(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200381 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100382 FileSystem delegate = getDelegate(path);
383 return delegate.isReadable(adjustPath(path, delegate));
384 }
385
386 @Override
aehligc801c392017-12-19 07:12:25 -0800387 protected void setReadable(Path path, boolean readable) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200388 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200389 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100390 FileSystem delegate = getDelegate(path);
391 delegate.setReadable(adjustPath(path, delegate), readable);
392 }
393
394 @Override
aehligc801c392017-12-19 07:12:25 -0800395 protected boolean isWritable(Path path) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200396 if (!supportsModifications(path)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100397 return false;
398 }
tomlu33700d62017-10-20 02:26:00 +0200399 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100400 FileSystem delegate = getDelegate(path);
401 return delegate.isWritable(adjustPath(path, delegate));
402 }
403
404 @Override
aehligc801c392017-12-19 07:12:25 -0800405 public void setWritable(Path path, boolean writable) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200406 checkModifiable(path);
tomlu33700d62017-10-20 02:26:00 +0200407 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100408 FileSystem delegate = getDelegate(path);
409 delegate.setWritable(adjustPath(path, delegate), writable);
410 }
411
412 @Override
aehligc801c392017-12-19 07:12:25 -0800413 protected boolean isExecutable(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200414 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100415 FileSystem delegate = getDelegate(path);
416 return delegate.isExecutable(adjustPath(path, delegate));
417 }
418
419 @Override
aehligc801c392017-12-19 07:12:25 -0800420 protected void setExecutable(Path path, boolean executable) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200421 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200422 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100423 FileSystem delegate = getDelegate(path);
424 delegate.setExecutable(adjustPath(path, delegate), executable);
425 }
426
427 @Override
aehligc801c392017-12-19 07:12:25 -0800428 protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200429 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100430 FileSystem delegate = getDelegate(path);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000431 return delegate.getFastDigest(adjustPath(path, delegate), hashFunction);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100432 }
433
434 @Override
aehligc801c392017-12-19 07:12:25 -0800435 public byte[] getxattr(Path path, String name) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200436 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100437 FileSystem delegate = getDelegate(path);
Nathan Harmatae1f187a2015-09-16 20:03:08 +0000438 return delegate.getxattr(adjustPath(path, delegate), name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100439 }
440
441 @Override
aehligc801c392017-12-19 07:12:25 -0800442 protected InputStream getInputStream(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200443 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100444 FileSystem delegate = getDelegate(path);
445 return delegate.getInputStream(adjustPath(path, delegate));
446 }
447
448 @Override
aehligc801c392017-12-19 07:12:25 -0800449 protected OutputStream getOutputStream(Path path, boolean append) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200450 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200451 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100452 FileSystem delegate = getDelegate(path);
453 return delegate.getOutputStream(adjustPath(path, delegate), append);
454 }
455
456 @Override
aehligc801c392017-12-19 07:12:25 -0800457 public void renameTo(Path sourcePath, Path targetPath) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200458 sourcePath = internalResolveSymlink(sourcePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100459 FileSystem sourceDelegate = getDelegate(sourcePath);
tomlu39fab102017-10-19 21:35:06 +0200460 if (!sourceDelegate.supportsModifications(sourcePath)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100461 throw new UnsupportedOperationException(
ccalvarinfb3293c2017-09-26 11:13:33 -0400462 String.format(
463 "The filesystem for the source path %s does not support modifications.",
464 sourcePath.getPathString()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100465 }
466 sourcePath = adjustPath(sourcePath, sourceDelegate);
467
468 FileSystem targetDelegate = getDelegate(targetPath);
tomlu39fab102017-10-19 21:35:06 +0200469 if (!targetDelegate.supportsModifications(targetPath)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100470 throw new UnsupportedOperationException(
ccalvarinfb3293c2017-09-26 11:13:33 -0400471 String.format(
472 "The filesystem for the target path %s does not support modifications.",
473 targetPath.getPathString()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100474 }
475 targetPath = adjustPath(targetPath, targetDelegate);
476
477 if (sourceDelegate == targetDelegate) {
478 // Easy, same filesystem.
479 sourceDelegate.renameTo(sourcePath, targetPath);
480 return;
481 } else {
482 // Copy across filesystems, then delete.
483 // copyFile throws on failure, so delete will never be reached if it fails.
aehligc801c392017-12-19 07:12:25 -0800484 FileSystemUtils.copyFile(sourcePath, targetPath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100485 sourceDelegate.delete(sourcePath);
486 }
487 }
Googlere1cd9502016-09-07 14:33:29 +0000488
489 @Override
aehligc801c392017-12-19 07:12:25 -0800490 protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200491 checkModifiable(linkPath);
Googlere1cd9502016-09-07 14:33:29 +0000492
tomlu33700d62017-10-20 02:26:00 +0200493 originalPath = internalResolveSymlink(originalPath);
Googlere1cd9502016-09-07 14:33:29 +0000494 FileSystem originalDelegate = getDelegate(originalPath);
495 FileSystem linkDelegate = getDelegate(linkPath);
496
tomlu39fab102017-10-19 21:35:06 +0200497 if (!originalDelegate.equals(linkDelegate)
498 || !linkDelegate.supportsHardLinksNatively(linkPath)) {
Googlere1cd9502016-09-07 14:33:29 +0000499 throw new UnsupportedOperationException(
500 "Attempted to create a hard link, but hard link support is disabled.");
501 }
502 linkDelegate.createFSDependentHardLink(
503 adjustPath(linkPath, linkDelegate), adjustPath(originalPath, originalDelegate));
504 }
tomlu33700d62017-10-20 02:26:00 +0200505
aehligc801c392017-12-19 07:12:25 -0800506 private Path internalResolveSymlink(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200507 while (isSymbolicLink(path)) {
aehligc801c392017-12-19 07:12:25 -0800508 PathFragment pathFragment = resolveOneLink(path);
tomlu33700d62017-10-20 02:26:00 +0200509 path = path.getRelative(pathFragment);
510 }
511 return path;
512 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100513}