blob: 8ef2760bb5b59a0744dbb084fc83a679852d9f53 [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
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.common.collect.Lists;
18import com.google.devtools.build.lib.concurrent.ThreadSafety;
Mark Schaller6df81792015-12-10 18:47:47 +000019import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import java.io.IOException;
21import java.io.InputStream;
22import java.io.OutputStream;
23import java.util.Collection;
24import java.util.Map;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import javax.annotation.Nullable;
26
27/**
28 * Presents a unified view of multiple virtual {@link FileSystem} instances, to which requests are
ccalvarinfb3293c2017-09-26 11:13:33 -040029 * delegated based on a {@link PathFragment} prefix mapping. If multiple prefixes apply to a given
30 * path, the *longest* (i.e. most specific) match is used. The order in which the delegates are
31 * specified does not influence the mapping.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032 *
ccalvarinfb3293c2017-09-26 11:13:33 -040033 * <p>Paths are preserved absolutely, contrary to how "mount" works, e.g.: /foo/bar maps to /foo/bar
34 * on the delegate, even if it is mounted at /foo.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035 *
ccalvarinfb3293c2017-09-26 11:13:33 -040036 * <p>For example: "/in" maps to InFileSystem, "/" maps to OtherFileSystem. Reading from
37 * "/in/base/BUILD" through the UnionFileSystem will delegate the read operation to InFileSystem,
38 * which will read "/in/base/BUILD" relative to its root. ("mount" behavior would remap it to
39 * "/base/BUILD" on the delegate).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040 *
ccalvarinfb3293c2017-09-26 11:13:33 -040041 * <p>Intra-filesystem symbolic links are resolved to their ultimate targets. Cross-filesystem links
42 * are not currently supported.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043 */
44@ThreadSafety.ThreadSafe
45public class UnionFileSystem extends FileSystem {
46
47 // Prefix trie index, allowing children to easily inherit prefix mappings
48 // of their parents.
49 // This does not currently handle unicode filenames.
ccalvarinfb3293c2017-09-26 11:13:33 -040050 private final PathTrie<FileSystem> pathDelegate;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051
Yun Peng352f7e72016-05-09 11:08:25 +000052 // True if the file path is case-sensitive on all the FileSystem
53 // or False if they are all case-insensitive, otherwise error.
54 private final boolean isCaseSensitive;
55
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056 /**
ccalvarinfb3293c2017-09-26 11:13:33 -040057 * Creates a new modifiable UnionFileSystem with prefix mappings specified by a map.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010058 *
59 * @param prefixMapping map of path prefixes to {@link FileSystem}s
tomlu121c6b12017-10-20 21:16:57 +020060 * @param rootFileSystem root for default requests; i.e. mapping of "/"
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010061 */
ccalvarinfb3293c2017-09-26 11:13:33 -040062 public UnionFileSystem(Map<PathFragment, FileSystem> prefixMapping, FileSystem rootFileSystem) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010063 super();
64 Preconditions.checkNotNull(prefixMapping);
65 Preconditions.checkNotNull(rootFileSystem);
66 Preconditions.checkArgument(rootFileSystem != this, "Circular root filesystem.");
67 Preconditions.checkArgument(
68 !prefixMapping.containsKey(PathFragment.EMPTY_FRAGMENT),
ccalvarinfb3293c2017-09-26 11:13:33 -040069 "Attempted to specify an explicit root prefix mapping; "
70 + "please use the rootFileSystem argument instead.");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071
ccalvarinfb3293c2017-09-26 11:13:33 -040072 this.pathDelegate = new PathTrie<>();
Yun Peng352f7e72016-05-09 11:08:25 +000073 this.isCaseSensitive = rootFileSystem.isFilePathCaseSensitive();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010074
75 for (Map.Entry<PathFragment, FileSystem> prefix : prefixMapping.entrySet()) {
76 FileSystem delegate = prefix.getValue();
Yun Peng352f7e72016-05-09 11:08:25 +000077 Preconditions.checkArgument(
78 delegate.isFilePathCaseSensitive() == this.isCaseSensitive,
79 "The case sensitiveness of FileSystem are different in UnionFileSystem");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010080 PathFragment prefixPath = prefix.getKey();
81
82 // Extra slash prevents within-directory mappings, which Path can't handle.
ccalvarinfb3293c2017-09-26 11:13:33 -040083 pathDelegate.put(prefixPath, delegate);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010084 }
ccalvarinfb3293c2017-09-26 11:13:33 -040085 pathDelegate.put(PathFragment.ROOT_FRAGMENT, rootFileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086 }
87
88 /**
ccalvarinfb3293c2017-09-26 11:13:33 -040089 * Retrieves the filesystem delegate of a path mapping. Does not follow symlinks (but you can call
90 * on a path preprocessed with {@link #resolveSymbolicLinks} to support this use case).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010091 *
92 * @param path the {@link Path} to map to a filesystem
93 * @throws IllegalArgumentException if no delegate exists for the path
94 */
95 protected FileSystem getDelegate(Path path) {
96 Preconditions.checkNotNull(path);
ccalvarinfb3293c2017-09-26 11:13:33 -040097 FileSystem immediateDelegate = pathDelegate.get(path.asFragment());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010098
99 // Should never actually happen if the root delegate is present.
ccalvarinfb3293c2017-09-26 11:13:33 -0400100 Preconditions.checkNotNull(immediateDelegate, "No delegate filesystem exists for %s", path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101 return immediateDelegate;
102 }
103
104 // Associates the path with the root of the given delegate filesystem.
105 // Necessary to avoid null pointer problems inside of the delegates.
106 protected Path adjustPath(Path path, FileSystem delegate) {
107 return delegate.getPath(path.asFragment());
108 }
109
110 /**
ccalvarinfb3293c2017-09-26 11:13:33 -0400111 * Follow a symbolic link once using the appropriate delegate filesystem, also resolving parent
112 * directory symlinks.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100113 *
114 * @param path {@link Path} to the symbolic link
115 */
116 @Override
117 protected PathFragment readSymbolicLink(Path path) throws IOException {
118 Preconditions.checkNotNull(path);
119 FileSystem delegate = getDelegate(path);
120 return delegate.readSymbolicLink(adjustPath(path, delegate));
121 }
122
123 @Override
124 protected PathFragment resolveOneLink(Path path) throws IOException {
125 Preconditions.checkNotNull(path);
126 FileSystem delegate = getDelegate(path);
127 return delegate.resolveOneLink(adjustPath(path, delegate));
128 }
129
tomlu39fab102017-10-19 21:35:06 +0200130 private void checkModifiable(Path path) {
131 if (!supportsModifications(path)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100132 throw new UnsupportedOperationException(
ccalvarinfb3293c2017-09-26 11:13:33 -0400133 String.format("Modifications to this %s are disabled.", getClass().getSimpleName()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100134 }
135 }
136
137 @Override
tomlu39fab102017-10-19 21:35:06 +0200138 public boolean supportsModifications(Path path) {
tomlu121c6b12017-10-20 21:16:57 +0200139 FileSystem delegate = getDelegate(path);
140 path = adjustPath(path, delegate);
141 return delegate.supportsModifications(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100142 }
143
144 @Override
tomlu39fab102017-10-19 21:35:06 +0200145 public boolean supportsSymbolicLinksNatively(Path path) {
tomlu121c6b12017-10-20 21:16:57 +0200146 FileSystem delegate = getDelegate(path);
147 path = adjustPath(path, delegate);
148 return delegate.supportsSymbolicLinksNatively(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100149 }
150
151 @Override
tomlu39fab102017-10-19 21:35:06 +0200152 public boolean supportsHardLinksNatively(Path path) {
tomlu121c6b12017-10-20 21:16:57 +0200153 FileSystem delegate = getDelegate(path);
154 path = adjustPath(path, delegate);
155 return delegate.supportsHardLinksNatively(path);
Googlere1cd9502016-09-07 14:33:29 +0000156 }
157
158 @Override
Yun Peng352f7e72016-05-09 11:08:25 +0000159 public boolean isFilePathCaseSensitive() {
tomlu121c6b12017-10-20 21:16:57 +0200160 return isCaseSensitive;
Yun Peng352f7e72016-05-09 11:08:25 +0000161 }
162
163 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100164 public String getFileSystemType(Path path) {
tomlu33700d62017-10-20 02:26:00 +0200165 try {
166 path = internalResolveSymlink(path);
167 } catch (IOException e) {
168 return "unknown";
169 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100170 FileSystem delegate = getDelegate(path);
171 return delegate.getFileSystemType(path);
172 }
173
174 @Override
olaolabfd1d332017-06-19 16:55:24 -0400175 protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200176 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100177 FileSystem delegate = getDelegate(path);
olaolabfd1d332017-06-19 16:55:24 -0400178 return delegate.getDigest(adjustPath(path, delegate), hashFunction);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100179 }
180
181 @Override
182 protected boolean createDirectory(Path path) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200183 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100184 // When creating the exact directory that is mapped,
185 // create it on both the parent's delegate and the path's delegate.
186 // This is necessary both for the parent to see the directory and for the
187 // delegate to use it.
188 // This is present to address this problematic case:
189 // / -> RootFs
190 // /foo -> FooFs
191 // mkdir /foo
192 // ls / ("foo" would be missing if not created on the parent)
193 // ls /foo (would fail if foo weren't also present on the child)
194 FileSystem delegate = getDelegate(path);
195 Path parent = path.getParentDirectory();
196 if (parent != null) {
tomlu33700d62017-10-20 02:26:00 +0200197 parent = internalResolveSymlink(parent);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 FileSystem parentDelegate = getDelegate(parent);
199 if (parentDelegate != delegate) {
200 // There's a possibility it already exists on the parent, so don't die
201 // if the directory can't be created there.
202 parentDelegate.createDirectory(adjustPath(path, parentDelegate));
203 }
204 }
205 return delegate.createDirectory(adjustPath(path, delegate));
206 }
207
208 @Override
209 protected long getFileSize(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200210 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200212 return delegate.getFileSize(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100213 }
214
215 @Override
216 protected boolean delete(Path path) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200217 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100218 FileSystem delegate = getDelegate(path);
219 return delegate.delete(adjustPath(path, delegate));
220 }
221
222 @Override
223 protected long getLastModifiedTime(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200224 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100225 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200226 return delegate.getLastModifiedTime(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100227 }
228
229 @Override
230 protected void setLastModifiedTime(Path path, long newTime) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200231 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200232 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100233 FileSystem delegate = getDelegate(path);
234 delegate.setLastModifiedTime(adjustPath(path, delegate), newTime);
235 }
236
237 @Override
238 protected boolean isSymbolicLink(Path path) {
239 FileSystem delegate = getDelegate(path);
240 path = adjustPath(path, delegate);
241 return delegate.isSymbolicLink(path);
242 }
243
244 @Override
245 protected boolean isDirectory(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200246 try {
247 path = followSymlinks ? internalResolveSymlink(path) : path;
248 } catch (IOException e) {
249 return false;
250 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100251 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200252 return delegate.isDirectory(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100253 }
254
255 @Override
256 protected boolean isFile(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200257 try {
258 path = followSymlinks ? internalResolveSymlink(path) : path;
259 } catch (IOException e) {
260 return false;
261 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100262 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200263 return delegate.isFile(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100264 }
265
266 @Override
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000267 protected boolean isSpecialFile(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200268 try {
269 path = followSymlinks ? internalResolveSymlink(path) : path;
270 } catch (IOException e) {
271 return false;
272 }
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000273 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200274 return delegate.isSpecialFile(adjustPath(path, delegate), false);
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000275 }
276
277 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100278 protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200279 checkModifiable(linkPath);
280 if (!supportsSymbolicLinksNatively(linkPath)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100281 throw new UnsupportedOperationException(
282 "Attempted to create a symlink, but symlink support is disabled.");
283 }
284
285 FileSystem delegate = getDelegate(linkPath);
286 delegate.createSymbolicLink(adjustPath(linkPath, delegate), targetFragment);
287 }
288
289 @Override
290 protected boolean exists(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200291 try {
292 path = followSymlinks ? internalResolveSymlink(path) : path;
293 } catch (IOException e) {
294 return false;
295 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100296 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200297 return delegate.exists(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100298 }
299
300 @Override
tomlu33700d62017-10-20 02:26:00 +0200301 protected FileStatus stat(Path path, boolean followSymlinks) throws IOException {
302 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100303 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200304 return delegate.stat(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100305 }
306
307 // Needs to be overridden for the delegation logic, because the
308 // UnixFileSystem implements statNullable and stat as separate codepaths.
309 // More generally, we wish to delegate all filesystem operations.
310 @Override
311 protected FileStatus statNullable(Path path, boolean followSymlinks) {
tomlu33700d62017-10-20 02:26:00 +0200312 try {
313 path = followSymlinks ? internalResolveSymlink(path) : path;
314 } catch (IOException e) {
315 return null;
316 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100317 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200318 return delegate.statNullable(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100319 }
320
321 @Override
322 @Nullable
323 protected FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200324 path = followSymlinks ? internalResolveSymlink(path) : path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100325 FileSystem delegate = getDelegate(path);
tomlu33700d62017-10-20 02:26:00 +0200326 return delegate.statIfFound(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100327 }
328
329 /**
ccalvarinfb3293c2017-09-26 11:13:33 -0400330 * Retrieves the directory entries for the specified path under the assumption that {@code
331 * resolvedPath} is the resolved path of {@code path} in one of the underlying file systems.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100332 *
333 * @param path the {@link Path} whose children are to be retrieved
334 */
335 @Override
336 protected Collection<Path> getDirectoryEntries(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200337 Path origPath = path;
338 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100339 FileSystem delegate = getDelegate(path);
340 Path resolvedPath = adjustPath(path, delegate);
341 Collection<Path> entries = resolvedPath.getDirectoryEntries();
342 Collection<Path> result = Lists.newArrayListWithCapacity(entries.size());
343 for (Path entry : entries) {
tomlu33700d62017-10-20 02:26:00 +0200344 result.add(origPath.getChild(entry.getBaseName()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100345 }
346 return result;
347 }
348
349 // No need for the more complex logic of getDirectoryEntries; it calls it implicitly.
350 @Override
351 protected Collection<Dirent> readdir(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.readdir(adjustPath(path, delegate), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100355 }
356
357 @Override
358 protected boolean isReadable(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200359 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100360 FileSystem delegate = getDelegate(path);
361 return delegate.isReadable(adjustPath(path, delegate));
362 }
363
364 @Override
365 protected void setReadable(Path path, boolean readable) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200366 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200367 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100368 FileSystem delegate = getDelegate(path);
369 delegate.setReadable(adjustPath(path, delegate), readable);
370 }
371
372 @Override
373 protected boolean isWritable(Path path) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200374 if (!supportsModifications(path)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100375 return false;
376 }
tomlu33700d62017-10-20 02:26:00 +0200377 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100378 FileSystem delegate = getDelegate(path);
379 return delegate.isWritable(adjustPath(path, delegate));
380 }
381
382 @Override
383 protected void setWritable(Path path, boolean writable) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200384 checkModifiable(path);
tomlu33700d62017-10-20 02:26:00 +0200385 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100386 FileSystem delegate = getDelegate(path);
387 delegate.setWritable(adjustPath(path, delegate), writable);
388 }
389
390 @Override
391 protected boolean isExecutable(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200392 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100393 FileSystem delegate = getDelegate(path);
394 return delegate.isExecutable(adjustPath(path, delegate));
395 }
396
397 @Override
398 protected void setExecutable(Path path, boolean executable) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200399 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200400 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100401 FileSystem delegate = getDelegate(path);
402 delegate.setExecutable(adjustPath(path, delegate), executable);
403 }
404
405 @Override
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000406 protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200407 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100408 FileSystem delegate = getDelegate(path);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000409 return delegate.getFastDigest(adjustPath(path, delegate), hashFunction);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100410 }
411
412 @Override
Nathan Harmatae1f187a2015-09-16 20:03:08 +0000413 protected byte[] getxattr(Path path, String name) 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);
Nathan Harmatae1f187a2015-09-16 20:03:08 +0000416 return delegate.getxattr(adjustPath(path, delegate), name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100417 }
418
419 @Override
420 protected InputStream getInputStream(Path path) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200421 path = internalResolveSymlink(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100422 FileSystem delegate = getDelegate(path);
423 return delegate.getInputStream(adjustPath(path, delegate));
424 }
425
426 @Override
427 protected OutputStream getOutputStream(Path path, boolean append) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200428 path = internalResolveSymlink(path);
tomlu39fab102017-10-19 21:35:06 +0200429 checkModifiable(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100430 FileSystem delegate = getDelegate(path);
431 return delegate.getOutputStream(adjustPath(path, delegate), append);
432 }
433
434 @Override
435 protected void renameTo(Path sourcePath, Path targetPath) throws IOException {
tomlu33700d62017-10-20 02:26:00 +0200436 sourcePath = internalResolveSymlink(sourcePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100437 FileSystem sourceDelegate = getDelegate(sourcePath);
tomlu39fab102017-10-19 21:35:06 +0200438 if (!sourceDelegate.supportsModifications(sourcePath)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100439 throw new UnsupportedOperationException(
ccalvarinfb3293c2017-09-26 11:13:33 -0400440 String.format(
441 "The filesystem for the source path %s does not support modifications.",
442 sourcePath.getPathString()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100443 }
444 sourcePath = adjustPath(sourcePath, sourceDelegate);
445
446 FileSystem targetDelegate = getDelegate(targetPath);
tomlu39fab102017-10-19 21:35:06 +0200447 if (!targetDelegate.supportsModifications(targetPath)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100448 throw new UnsupportedOperationException(
ccalvarinfb3293c2017-09-26 11:13:33 -0400449 String.format(
450 "The filesystem for the target path %s does not support modifications.",
451 targetPath.getPathString()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100452 }
453 targetPath = adjustPath(targetPath, targetDelegate);
454
455 if (sourceDelegate == targetDelegate) {
456 // Easy, same filesystem.
457 sourceDelegate.renameTo(sourcePath, targetPath);
458 return;
459 } else {
460 // Copy across filesystems, then delete.
461 // copyFile throws on failure, so delete will never be reached if it fails.
462 FileSystemUtils.copyFile(sourcePath, targetPath);
463 sourceDelegate.delete(sourcePath);
464 }
465 }
Googlere1cd9502016-09-07 14:33:29 +0000466
467 @Override
ccalvarinfb3293c2017-09-26 11:13:33 -0400468 protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException {
tomlu39fab102017-10-19 21:35:06 +0200469 checkModifiable(linkPath);
Googlere1cd9502016-09-07 14:33:29 +0000470
tomlu33700d62017-10-20 02:26:00 +0200471 originalPath = internalResolveSymlink(originalPath);
Googlere1cd9502016-09-07 14:33:29 +0000472 FileSystem originalDelegate = getDelegate(originalPath);
473 FileSystem linkDelegate = getDelegate(linkPath);
474
tomlu39fab102017-10-19 21:35:06 +0200475 if (!originalDelegate.equals(linkDelegate)
476 || !linkDelegate.supportsHardLinksNatively(linkPath)) {
Googlere1cd9502016-09-07 14:33:29 +0000477 throw new UnsupportedOperationException(
478 "Attempted to create a hard link, but hard link support is disabled.");
479 }
480 linkDelegate.createFSDependentHardLink(
481 adjustPath(linkPath, linkDelegate), adjustPath(originalPath, originalDelegate));
482 }
tomlu33700d62017-10-20 02:26:00 +0200483
484 private Path internalResolveSymlink(Path path) throws IOException {
485 while (isSymbolicLink(path)) {
486 PathFragment pathFragment = resolveOneLink(path);
487 path = path.getRelative(pathFragment);
488 }
489 return path;
490 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100491}