blob: 2169c18b9c2634060e67b9d23ee016d3b447a5a5 [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
17import static java.nio.charset.StandardCharsets.ISO_8859_1;
18
buchgr559a07d2017-11-30 11:09:35 -080019import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.Lists;
21import com.google.common.hash.Hashing;
22import com.google.common.io.ByteSource;
23import com.google.common.io.CharStreams;
24import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000025import com.google.devtools.common.options.EnumConverter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import java.io.FileNotFoundException;
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.InputStreamReader;
30import java.io.OutputStream;
Googlere1cd9502016-09-07 14:33:29 +000031import java.nio.file.FileAlreadyExistsException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032import java.util.Collection;
33import java.util.List;
34
35/**
36 * This interface models a file system using UNIX the naming scheme.
37 */
38@ThreadSafe
39public abstract class FileSystem {
aehligc801c392017-12-19 07:12:25 -080040
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000041 /** Type of hash function to use for digesting files. */
olaolabfd1d332017-06-19 16:55:24 -040042 // The underlying HashFunctions are immutable and thread safe.
43 @SuppressWarnings("ImmutableEnumChecker")
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000044 public enum HashFunction {
olaolabfd1d332017-06-19 16:55:24 -040045 MD5(Hashing.md5()),
46 SHA1(Hashing.sha1()),
47 SHA256(Hashing.sha256());
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000048
olaolabfd1d332017-06-19 16:55:24 -040049 private final com.google.common.hash.HashFunction hash;
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000050
olaolabfd1d332017-06-19 16:55:24 -040051 HashFunction(com.google.common.hash.HashFunction hash) {
52 this.hash = hash;
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000053 }
54
55 /** Converts to {@link HashFunction}. */
56 public static class Converter extends EnumConverter<HashFunction> {
57 public Converter() {
58 super(HashFunction.class, "hash function");
59 }
60 }
61
olaolabfd1d332017-06-19 16:55:24 -040062 public com.google.common.hash.HashFunction getHash() {
63 return hash;
64 }
65
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000066 public boolean isValidDigest(byte[] digest) {
olaolabfd1d332017-06-19 16:55:24 -040067 return digest != null && digest.length * 8 == hash.bits();
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000068 }
69 }
70
buchgr559a07d2017-11-30 11:09:35 -080071 private final HashFunction digestFunction;
72
73 public FileSystem() {
74 this(HashFunction.MD5);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000075 }
76
buchgr559a07d2017-11-30 11:09:35 -080077 public FileSystem(HashFunction digestFunction) {
78 this.digestFunction = Preconditions.checkNotNull(digestFunction);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000079 }
80
buchgr559a07d2017-11-30 11:09:35 -080081 public HashFunction getDigestFunction() {
olaoladfb01032017-06-14 18:40:31 +020082 return digestFunction;
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000083 }
84
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010085 /**
86 * An exception thrown when attempting to resolve an ordinary file as a symlink.
87 */
88 protected static final class NotASymlinkException extends IOException {
aehligc801c392017-12-19 07:12:25 -080089 public NotASymlinkException(Path path) {
90 super(path.toString());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010091 }
92 }
93
tomlu4c9fafd2018-01-18 10:29:11 -080094 private final Root absoluteRoot = new Root.AbsoluteRoot(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095
tomlua729b9b2018-02-08 15:32:00 -080096 /**
97 * Returns an absolute path instance, given an absolute path name, without double slashes, .., or
98 * . segments. While this method will normalize the path representation by creating a
99 * structured/parsed representation, it will not cause any IO. (e.g., it will not resolve symbolic
100 * links if it's a Unix file system.
101 */
102 public Path getPath(String path) {
103 return Path.create(path, this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100104 }
105
tomlua729b9b2018-02-08 15:32:00 -0800106 /** Returns an absolute path instance, given an absolute path fragment. */
107 public Path getPath(PathFragment pathFragment) {
108 Preconditions.checkArgument(pathFragment.isAbsolute());
109 return Path.createAlreadyNormalized(
110 pathFragment.getPathString(), pathFragment.getDriveStrLength(), this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100111 }
112
tomlu4c9fafd2018-01-18 10:29:11 -0800113 final Root getAbsoluteRoot() {
114 return absoluteRoot;
tomluee6a6862018-01-17 14:36:26 -0800115 }
116
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100117 /**
tomlu39fab102017-10-19 21:35:06 +0200118 * Returns whether or not the FileSystem supports modifications of files and file entries.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100119 *
120 * <p>Returns true if FileSystem supports the following:
tomlu39fab102017-10-19 21:35:06 +0200121 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100122 * <ul>
aehligc801c392017-12-19 07:12:25 -0800123 * <li>{@link #setWritable(Path, boolean)}
124 * <li>{@link #setExecutable(Path, boolean)}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100125 * </ul>
126 *
tomlu39fab102017-10-19 21:35:06 +0200127 * The above calls will result in an {@link UnsupportedOperationException} on a FileSystem where
128 * this method returns {@code false}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100129 */
aehligc801c392017-12-19 07:12:25 -0800130 public abstract boolean supportsModifications(Path path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100131
132 /**
133 * Returns whether or not the FileSystem supports symbolic links.
134 *
135 * <p>Returns true if FileSystem supports the following:
tomlu39fab102017-10-19 21:35:06 +0200136 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100137 * <ul>
aehligc801c392017-12-19 07:12:25 -0800138 * <li>{@link #createSymbolicLink(Path, PathFragment)}
139 * <li>{@link #getFileSize(Path, boolean)} where {@code followSymlinks=false}
140 * <li>{@link #getLastModifiedTime(Path, boolean)} where {@code followSymlinks=false}
141 * <li>{@link #readSymbolicLink(Path)} where the link points to a non-existent file
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100142 * </ul>
143 *
tomlu39fab102017-10-19 21:35:06 +0200144 * The above calls may result in an {@link UnsupportedOperationException} on a FileSystem where
145 * this method returns {@code false}. The implementation can try to emulate these calls at its own
146 * discretion.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147 */
aehligc801c392017-12-19 07:12:25 -0800148 public abstract boolean supportsSymbolicLinksNatively(Path path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100149
Googlere1cd9502016-09-07 14:33:29 +0000150 /**
151 * Returns whether or not the FileSystem supports hard links.
152 *
153 * <p>Returns true if FileSystem supports the following:
154 *
155 * <ul>
aehligc801c392017-12-19 07:12:25 -0800156 * <li>{@link #createFSDependentHardLink(Path, Path)}
Googlere1cd9502016-09-07 14:33:29 +0000157 * </ul>
158 *
159 * The above calls may result in an {@link UnsupportedOperationException} on a FileSystem where
160 * this method returns {@code false}. The implementation can try to emulate these calls at its own
161 * discretion.
162 */
aehligc801c392017-12-19 07:12:25 -0800163 protected abstract boolean supportsHardLinksNatively(Path path);
Googlere1cd9502016-09-07 14:33:29 +0000164
Yun Peng352f7e72016-05-09 11:08:25 +0000165 /***
166 * Returns true if file path is case-sensitive on this file system. Default is true.
167 */
168 public abstract boolean isFilePathCaseSensitive();
169
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100170 /**
171 * Returns the type of the file system path belongs to.
172 *
aehligc801c392017-12-19 07:12:25 -0800173 * <p>The string returned is obtained directly from the operating system, so
174 * it's a best guess in absence of a guaranteed api.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 *
aehligc801c392017-12-19 07:12:25 -0800176 * <p>This implementation uses <code>/proc/mounts</code> to determine the
177 * file system type.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100178 */
aehligc801c392017-12-19 07:12:25 -0800179 public String getFileSystemType(Path path) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100180 String fileSystem = "unknown";
181 int bestMountPointSegmentCount = -1;
182 try {
aehligc801c392017-12-19 07:12:25 -0800183 Path canonicalPath = path.resolveSymbolicLinks();
184 Path mountTable = path.getRelative("/proc/mounts");
185 try (InputStreamReader reader = new InputStreamReader(mountTable.getInputStream(),
186 ISO_8859_1)) {
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000187 for (String line : CharStreams.readLines(reader)) {
188 String[] words = line.split("\\s+");
189 if (words.length >= 3) {
190 if (!words[1].startsWith("/")) {
191 continue;
192 }
aehligc801c392017-12-19 07:12:25 -0800193 Path mountPoint = path.getFileSystem().getPath(words[1]);
194 int segmentCount = mountPoint.asFragment().segmentCount();
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000195 if (canonicalPath.startsWith(mountPoint) && segmentCount > bestMountPointSegmentCount) {
196 bestMountPointSegmentCount = segmentCount;
197 fileSystem = words[2];
198 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100199 }
200 }
201 }
202 } catch (IOException e) {
203 // pass
204 }
205 return fileSystem;
206 }
207
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100208 /**
tomlu52aff7d2017-12-11 18:23:51 -0800209 * Creates a directory with the name of the current path. See {@link Path#createDirectory} for
210 * specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 */
aehligc801c392017-12-19 07:12:25 -0800212 public abstract boolean createDirectory(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100213
214 /**
tomludecca2b2017-12-21 08:59:51 -0800215 * Creates all directories up to the path. See {@link Path#createDirectoryAndParents} for
216 * specification.
217 */
218 public abstract void createDirectoryAndParents(Path path) throws IOException;
219
220 /**
tomlu39fab102017-10-19 21:35:06 +0200221 * Returns the size in bytes of the file denoted by {@code path}. See {@link
222 * Path#getFileSize(Symlinks)} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100223 *
aehligc801c392017-12-19 07:12:25 -0800224 * <p>Note: for <@link FileSystem>s where {@link #supportsSymbolicLinksNatively(Path)} returns
225 * false, this method will throw an {@link UnsupportedOperationException} if {@code
tomlu39fab102017-10-19 21:35:06 +0200226 * followSymLinks=false}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100227 */
aehligc801c392017-12-19 07:12:25 -0800228 protected abstract long getFileSize(Path path, boolean followSymlinks) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100229
tomlu52aff7d2017-12-11 18:23:51 -0800230 /** Deletes the file denoted by {@code path}. See {@link Path#delete} for specification. */
aehligc801c392017-12-19 07:12:25 -0800231 public abstract boolean delete(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100232
233 /**
tomlu39fab102017-10-19 21:35:06 +0200234 * Returns the last modification time of the file denoted by {@code path}. See {@link
235 * Path#getLastModifiedTime(Symlinks)} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100236 *
aehligc801c392017-12-19 07:12:25 -0800237 * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(Path)} returns
238 * false, this method will throw an {@link UnsupportedOperationException} if {@code
tomlu39fab102017-10-19 21:35:06 +0200239 * followSymLinks=false}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100240 */
aehligc801c392017-12-19 07:12:25 -0800241 protected abstract long getLastModifiedTime(Path path, boolean followSymlinks) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100242
243 /**
tomluc2f6ae82017-12-08 14:19:42 -0800244 * Sets the last modification time of the file denoted by {@code path}. See {@link
245 * Path#setLastModifiedTime} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100246 */
aehligc801c392017-12-19 07:12:25 -0800247 public abstract void setLastModifiedTime(Path path, long newTime) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100248
249 /**
aehligc801c392017-12-19 07:12:25 -0800250 * Returns value of the given extended attribute name or null if attribute
251 * does not exist or file system does not support extended attributes. Follows symlinks.
252 * <p>Default implementation assumes that file system does not support
253 * extended attributes and always returns null. Specific file system
254 * implementations should override this method if they do provide support
255 * for extended attributes.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100256 *
257 * @param path the file whose extended attribute is to be returned.
258 * @param name the name of the extended attribute key.
aehligc801c392017-12-19 07:12:25 -0800259 * @return the value of the extended attribute associated with 'path', if
260 * any, or null if no such attribute is defined (ENODATA) or file
261 * system does not support extended attributes at all.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100262 * @throws IOException if the call failed for any other reason.
263 */
aehligc801c392017-12-19 07:12:25 -0800264 public byte[] getxattr(Path path, String name) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100265 return null;
266 }
267
268 /**
aehligc801c392017-12-19 07:12:25 -0800269 * Gets a fast digest for the given path and hash function type, or {@code null} if there
270 * isn't one available or the filesystem doesn't support them. This digest should be
271 * suitable for detecting changes to the file.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100272 */
aehligc801c392017-12-19 07:12:25 -0800273 protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100274 return null;
275 }
276
277 /**
278 * Gets a fast digest for the given path, or {@code null} if there isn't one available or the
279 * filesystem doesn't support them. This digest should be suitable for detecting changes to the
280 * file.
281 */
aehligc801c392017-12-19 07:12:25 -0800282 protected final byte[] getFastDigest(Path path) throws IOException {
olaoladfb01032017-06-14 18:40:31 +0200283 return getFastDigest(path, digestFunction);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000284 }
285
286 /**
287 * Returns whether the given digest is a valid digest for the default digest function.
288 */
289 public boolean isValidDigest(byte[] digest) {
olaoladfb01032017-06-14 18:40:31 +0200290 return digestFunction.isValidDigest(digest);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000291 }
292
293 /**
aehligc801c392017-12-19 07:12:25 -0800294 * Returns the digest of the file denoted by the path, following
295 * symbolic links, for the given hash digest function.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000296 *
297 * @return a new byte array containing the file's digest
298 * @throws IOException if the digest could not be computed for any reason
aehligc801c392017-12-19 07:12:25 -0800299 *
300 * Subclasses may (and do) optimize this computation for particular digest functions.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000301 */
aehligc801c392017-12-19 07:12:25 -0800302 protected byte[] getDigest(final Path path, HashFunction hashFunction) throws IOException {
olaolabfd1d332017-06-19 16:55:24 -0400303 return new ByteSource() {
304 @Override
305 public InputStream openStream() throws IOException {
306 return getInputStream(path);
307 }
308 }.hash(hashFunction.getHash()).asBytes();
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000309 }
310
311 /**
312 * Returns the digest of the file denoted by the path, following symbolic links.
313 *
314 * @return a new byte array containing the file's digest
315 * @throws IOException if the digest could not be computed for any reason
316 */
aehligc801c392017-12-19 07:12:25 -0800317 protected final byte[] getDigest(final Path path) throws IOException {
olaoladfb01032017-06-14 18:40:31 +0200318 return getDigest(path, digestFunction);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100319 }
320
321 /**
aehligc801c392017-12-19 07:12:25 -0800322 * Returns true if "path" denotes an existing symbolic link. See
323 * {@link Path#isSymbolicLink} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100324 */
aehligc801c392017-12-19 07:12:25 -0800325 protected abstract boolean isSymbolicLink(Path path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100326
327 /**
aehligc801c392017-12-19 07:12:25 -0800328 * Appends a single regular path segment 'child' to 'dir', recursively
329 * resolving symbolic links in 'child'. 'dir' must be canonical. 'maxLinks' is
330 * the maximum number of symbolic links that may be traversed before it gives
331 * up (the Linux kernel uses 32).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100332 *
aehligc801c392017-12-19 07:12:25 -0800333 * <p>(This method does not need to be synchronized; but the result may be
334 * stale in the case of concurrent modification.)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100335 *
aehligc801c392017-12-19 07:12:25 -0800336 * @throws IOException if 'dir' is not an existing directory; or if
337 * stat(child) fails for any reason, or if 'child' is a symlink and
338 * readlink(child) fails for any reason (e.g. ENOENT, EACCES), or if
339 * the chain of symbolic links exceeds 'maxLinks'.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100340 */
aehligc801c392017-12-19 07:12:25 -0800341 protected final Path appendSegment(Path dir, String child, int maxLinks) throws IOException {
342 Path naive = dir.getChild(child);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100343
aehligc801c392017-12-19 07:12:25 -0800344 PathFragment linkTarget = resolveOneLink(naive);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100345 if (linkTarget == null) {
346 return naive; // regular file or directory
347 }
348
349 if (maxLinks-- == 0) {
350 throw new IOException(naive + " (Too many levels of symbolic links)");
351 }
aehligc801c392017-12-19 07:12:25 -0800352 if (linkTarget.isAbsolute()) {
tomlua729b9b2018-02-08 15:32:00 -0800353 dir = getPath(linkTarget.getDriveStr());
tomlu51de88c2017-10-20 20:59:44 +0200354 }
aehligc801c392017-12-19 07:12:25 -0800355 for (String name : linkTarget.segments()) {
Ulf Adams07dba942015-03-05 14:47:37 +0000356 if (name.equals(".") || name.isEmpty()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100357 // no-op
358 } else if (name.equals("..")) {
aehligc801c392017-12-19 07:12:25 -0800359 Path parent = dir.getParentDirectory();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100360 // root's parent is root, when canonicalizing, so this is a no-op.
361 if (parent != null) { dir = parent; }
362 } else {
363 dir = appendSegment(dir, name, maxLinks);
364 }
365 }
366 return dir;
367 }
368
369 /**
aehligc801c392017-12-19 07:12:25 -0800370 * Helper method of {@link #resolveSymbolicLinks(Path)}. This method
371 * encapsulates the I/O component of a full canonicalization operation.
372 * Subclasses can (and do) provide more efficient implementations.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100373 *
aehligc801c392017-12-19 07:12:25 -0800374 * <p>(This method does not need to be synchronized; but the result may be
375 * stale in the case of concurrent modification.)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100376 *
aehligc801c392017-12-19 07:12:25 -0800377 * @param path a path, of which all but the last segment is guaranteed to be
378 * canonical
379 * @return {@link #readSymbolicLink} iff path is a symlink or null iff
380 * path exists but is not a symlink
381 * @throws IOException if the file did not exist, or a parent directory could
382 * not be searched
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100383 */
aehligc801c392017-12-19 07:12:25 -0800384 protected PathFragment resolveOneLink(Path path) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100385 try {
386 return readSymbolicLink(path);
387 } catch (NotASymlinkException e) {
388 // Not a symbolic link. Check it exists.
389
390 // (A simple call to lstat would replace all of this.)
391 if (!exists(path, false)) {
392 throw new FileNotFoundException(path + " (No such file or directory)");
393 }
394
395 // TODO(bazel-team): (2009) ideally, throw ENOTDIR if dir is not a dir, but that
396 // would require twice as many stats, or a much more convoluted
397 // implementation (like glibc's canonicalize.c).
398
399 return null; // exists.
400 }
401 }
402
403 /**
aehligc801c392017-12-19 07:12:25 -0800404 * Returns the canonical path for the given path. See
405 * {@link Path#resolveSymbolicLinks} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100406 */
aehligc801c392017-12-19 07:12:25 -0800407 protected Path resolveSymbolicLinks(Path path)
408 throws IOException {
409 Path parentNode = path.getParentDirectory();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100410 return parentNode == null
411 ? path // (root)
412 : appendSegment(resolveSymbolicLinks(parentNode), path.getBaseName(), 32);
413 }
414
415 /**
aehligc801c392017-12-19 07:12:25 -0800416 * Returns the status of a file. See {@link Path#stat(Symlinks)} for
417 * specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100418 *
aehligc801c392017-12-19 07:12:25 -0800419 * <p>The default implementation of this method is a "lazy" one, based on
420 * other accessor methods such as {@link #isFile}, etc. Subclasses may provide
421 * more efficient specializations. However, we still try to follow Unix-like
422 * semantics of failing fast in case of non-existent files (or in case of
423 * permission issues).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100424 */
aehligc801c392017-12-19 07:12:25 -0800425 protected FileStatus stat(final Path path, final boolean followSymlinks) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100426 FileStatus status = new FileStatus() {
427 volatile Boolean isFile;
428 volatile Boolean isDirectory;
429 volatile Boolean isSymbolicLink;
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000430 volatile Boolean isSpecial;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100431 volatile long size = -1;
432 volatile long mtime = -1;
433
434 @Override
435 public boolean isFile() {
436 if (isFile == null) { isFile = FileSystem.this.isFile(path, followSymlinks); }
437 return isFile;
438 }
439
440 @Override
441 public boolean isDirectory() {
442 if (isDirectory == null) {
443 isDirectory = FileSystem.this.isDirectory(path, followSymlinks);
444 }
445 return isDirectory;
446 }
447
448 @Override
449 public boolean isSymbolicLink() {
450 if (isSymbolicLink == null) { isSymbolicLink = FileSystem.this.isSymbolicLink(path); }
451 return isSymbolicLink;
452 }
453
454 @Override
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000455 public boolean isSpecialFile() {
456 if (isSpecial == null) { isSpecial = FileSystem.this.isSpecialFile(path, followSymlinks); }
457 return isSpecial;
458 }
459
460 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100461 public long getSize() throws IOException {
462 if (size == -1) { size = getFileSize(path, followSymlinks); }
463 return size;
464 }
465
466 @Override
467 public long getLastModifiedTime() throws IOException {
468 if (mtime == -1) { mtime = FileSystem.this.getLastModifiedTime(path, followSymlinks); }
469 return mtime;
470 }
471
472 @Override
473 public long getLastChangeTime() {
474 throw new UnsupportedOperationException();
475 }
476
477 @Override
478 public long getNodeId() {
479 throw new UnsupportedOperationException();
480 }
481 };
482
483 // Fail fast in case if some operations will actually fail, since stat() call sometimes used
484 // to verify file existence as well. We will use getLastModifiedTime() method for that purpose.
485 status.getLastModifiedTime();
486
487 return status;
488 }
489
aehligc801c392017-12-19 07:12:25 -0800490 /**
491 * Like stat(), but returns null on failures instead of throwing.
492 */
493 protected FileStatus statNullable(Path path, boolean followSymlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100494 try {
495 return stat(path, followSymlinks);
496 } catch (IOException e) {
497 return null;
498 }
499 }
500
501 /**
aehligc801c392017-12-19 07:12:25 -0800502 * Like {@link #stat}, but returns null if the file is not found (corresponding to
503 * {@code ENOENT} or {@code ENOTDIR} in Unix's stat(2) function) instead of throwing. Note that
504 * this implementation does <i>not</i> successfully catch {@code ENOTDIR} exceptions. If the
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100505 * instantiated filesystem can catch such errors, it should override this method to do so.
506 */
aehligc801c392017-12-19 07:12:25 -0800507 protected FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100508 try {
509 return stat(path, followSymlinks);
510 } catch (FileNotFoundException e) {
511 return null;
512 }
513 }
514
515 /**
aehligc801c392017-12-19 07:12:25 -0800516 * Returns true iff {@code path} denotes an existing directory. See
517 * {@link Path#isDirectory(Symlinks)} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100518 */
aehligc801c392017-12-19 07:12:25 -0800519 protected abstract boolean isDirectory(Path path, boolean followSymlinks);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100520
521 /**
aehligc801c392017-12-19 07:12:25 -0800522 * Returns true iff {@code path} denotes an existing regular or special file.
523 * See {@link Path#isFile(Symlinks)} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100524 */
aehligc801c392017-12-19 07:12:25 -0800525 protected abstract boolean isFile(Path path, boolean followSymlinks);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100526
527 /**
aehligc801c392017-12-19 07:12:25 -0800528 * Returns true iff {@code path} denotes a special file.
529 * See {@link Path#isSpecialFile(Symlinks)} for specification.
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000530 */
aehligc801c392017-12-19 07:12:25 -0800531 protected abstract boolean isSpecialFile(Path path, boolean followSymlinks);
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000532
533 /**
tomlu39fab102017-10-19 21:35:06 +0200534 * Creates a symbolic link. See {@link Path#createSymbolicLink(Path)} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100535 *
aehligc801c392017-12-19 07:12:25 -0800536 * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(Path)} returns
537 * false, this method will throw an {@link UnsupportedOperationException}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100538 */
aehligc801c392017-12-19 07:12:25 -0800539 protected abstract void createSymbolicLink(Path linkPath, PathFragment targetFragment)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100540 throws IOException;
541
542 /**
tomlu39fab102017-10-19 21:35:06 +0200543 * Returns the target of a symbolic link. See {@link Path#readSymbolicLink} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100544 *
aehligc801c392017-12-19 07:12:25 -0800545 * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(Path)} returns
546 * false, this method will throw an {@link UnsupportedOperationException} if the link points to a
547 * non-existent file.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100548 *
549 * @throws NotASymlinkException if the current path is not a symbolic link
550 * @throws IOException if the contents of the link could not be read for any reason.
551 */
aehligc801c392017-12-19 07:12:25 -0800552 protected abstract PathFragment readSymbolicLink(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100553
554 /**
Nathan Harmata215974e52015-09-16 21:31:49 +0000555 * Returns the target of a symbolic link, under the assumption that the given path is indeed a
aehligc801c392017-12-19 07:12:25 -0800556 * symbolic link (this assumption permits efficient implementations). See
557 * {@link Path#readSymbolicLinkUnchecked} for specification.
Nathan Harmata215974e52015-09-16 21:31:49 +0000558 *
559 * @throws IOException if the contents of the link could not be read for any reason.
560 */
aehligc801c392017-12-19 07:12:25 -0800561 protected PathFragment readSymbolicLinkUnchecked(Path path) throws IOException {
Nathan Harmata215974e52015-09-16 21:31:49 +0000562 return readSymbolicLink(path);
563 }
564
tomluc2f6ae82017-12-08 14:19:42 -0800565 /** Returns true iff this path denotes an existing file of any kind. Follows symbolic links. */
aehligc801c392017-12-19 07:12:25 -0800566 public boolean exists(Path path) {
tomluc2f6ae82017-12-08 14:19:42 -0800567 return exists(path, true);
568 }
569
Nathan Harmata215974e52015-09-16 21:31:49 +0000570 /**
aehligc801c392017-12-19 07:12:25 -0800571 * Returns true iff {@code path} denotes an existing file of any kind. See
572 * {@link Path#exists(Symlinks)} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100573 */
aehligc801c392017-12-19 07:12:25 -0800574 protected abstract boolean exists(Path path, boolean followSymlinks);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100575
576 /**
tomlu0a82e702017-10-23 18:16:44 +0200577 * Returns a collection containing the names of all entities within the directory denoted by the
578 * {@code path}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100579 *
580 * @throws IOException if there was an error reading the directory entries
581 */
aehligc801c392017-12-19 07:12:25 -0800582 protected abstract Collection<String> getDirectoryEntries(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100583
Eric Fellheimerfb6e5e92015-06-02 20:16:57 +0000584 protected static Dirent.Type direntFromStat(FileStatus stat) {
585 if (stat == null) {
jcater0a57d3d2018-05-01 20:33:18 -0700586 return Dirent.Type.UNKNOWN;
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000587 } else if (stat.isSpecialFile()) {
jcater0a57d3d2018-05-01 20:33:18 -0700588 return Dirent.Type.UNKNOWN;
Eric Fellheimerfb6e5e92015-06-02 20:16:57 +0000589 } else if (stat.isFile()) {
jcater0a57d3d2018-05-01 20:33:18 -0700590 return Dirent.Type.FILE;
Eric Fellheimerfb6e5e92015-06-02 20:16:57 +0000591 } else if (stat.isDirectory()) {
jcater0a57d3d2018-05-01 20:33:18 -0700592 return Dirent.Type.DIRECTORY;
Eric Fellheimerfb6e5e92015-06-02 20:16:57 +0000593 } else if (stat.isSymbolicLink()) {
jcater0a57d3d2018-05-01 20:33:18 -0700594 return Dirent.Type.SYMLINK;
Eric Fellheimerfb6e5e92015-06-02 20:16:57 +0000595 } else {
jcater0a57d3d2018-05-01 20:33:18 -0700596 return Dirent.Type.UNKNOWN;
Eric Fellheimerfb6e5e92015-06-02 20:16:57 +0000597 }
598 }
599
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100600 /**
aehligc801c392017-12-19 07:12:25 -0800601 * Returns a Dirents structure, listing the names of all entries within the
602 * directory {@code path}, plus their types (file, directory, other).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100603 *
aehligc801c392017-12-19 07:12:25 -0800604 * @param followSymlinks whether to follow symlinks when determining the file types of
605 * individual directory entries. No matter the value of this parameter, symlinks are
606 * followed when resolving the directory whose entries are to be read.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100607 * @throws IOException if there was an error reading the directory entries
608 */
aehligc801c392017-12-19 07:12:25 -0800609 protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
tomlu0a82e702017-10-23 18:16:44 +0200610 Collection<String> children = getDirectoryEntries(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100611 List<Dirent> dirents = Lists.newArrayListWithCapacity(children.size());
tomlu0a82e702017-10-23 18:16:44 +0200612 for (String child : children) {
aehligc801c392017-12-19 07:12:25 -0800613 Path childPath = path.getChild(child);
tomlu0a82e702017-10-23 18:16:44 +0200614 Dirent.Type type = direntFromStat(statNullable(childPath, followSymlinks));
615 dirents.add(new Dirent(child, type));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100616 }
617 return dirents;
618 }
619
620 /**
621 * Returns true iff the file represented by {@code path} is readable.
622 *
623 * @throws IOException if there was an error reading the file's metadata
624 */
aehligc801c392017-12-19 07:12:25 -0800625 protected abstract boolean isReadable(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100626
627 /**
tomlu39fab102017-10-19 21:35:06 +0200628 * Sets the file to readable (if the argument is true) or non-readable (if the argument is false)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100629 *
aehligc801c392017-12-19 07:12:25 -0800630 * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false or
631 * which do not support unreadable files, this method will throw an {@link
tomlu39fab102017-10-19 21:35:06 +0200632 * UnsupportedOperationException}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100633 *
634 * @throws IOException if there was an error reading or writing the file's metadata
635 */
aehligc801c392017-12-19 07:12:25 -0800636 protected abstract void setReadable(Path path, boolean readable) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100637
638 /**
639 * Returns true iff the file represented by {@code path} is writable.
640 *
641 * @throws IOException if there was an error reading the file's metadata
642 */
aehligc801c392017-12-19 07:12:25 -0800643 protected abstract boolean isWritable(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100644
645 /**
tomlu39fab102017-10-19 21:35:06 +0200646 * Sets the file to writable (if the argument is true) or non-writable (if the argument is false)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100647 *
aehligc801c392017-12-19 07:12:25 -0800648 * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false, this
649 * method will throw an {@link UnsupportedOperationException}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100650 *
651 * @throws IOException if there was an error reading or writing the file's metadata
652 */
aehligc801c392017-12-19 07:12:25 -0800653 public abstract void setWritable(Path path, boolean writable) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100654
655 /**
656 * Returns true iff the file represented by the path is executable.
657 *
658 * @throws IOException if there was an error reading the file's metadata
659 */
aehligc801c392017-12-19 07:12:25 -0800660 protected abstract boolean isExecutable(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100661
662 /**
tomlu39fab102017-10-19 21:35:06 +0200663 * Sets the file to executable, if the argument is true. It is currently not supported to unset
664 * the executable status of a file, so {code executable=false} yields an {@link
665 * UnsupportedOperationException}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100666 *
aehligc801c392017-12-19 07:12:25 -0800667 * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false, this
668 * method will throw an {@link UnsupportedOperationException}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100669 *
670 * @throws IOException if there was an error reading or writing the file's metadata
671 */
aehligc801c392017-12-19 07:12:25 -0800672 protected abstract void setExecutable(Path path, boolean executable) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100673
674 /**
tomlu39fab102017-10-19 21:35:06 +0200675 * Sets the file permissions. If permission changes on this {@link FileSystem} are slow (e.g. one
676 * syscall per change), this method should aim to be faster than setting each permission
677 * individually. If this {@link FileSystem} does not support group or others permissions, those
678 * bits will be ignored.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100679 *
aehligc801c392017-12-19 07:12:25 -0800680 * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false, this
681 * method will throw an {@link UnsupportedOperationException}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100682 *
683 * @throws IOException if there was an error reading or writing the file's metadata
684 */
aehligc801c392017-12-19 07:12:25 -0800685 protected void chmod(Path path, int mode) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100686 setReadable(path, (mode & 0400) != 0);
687 setWritable(path, (mode & 0200) != 0);
688 setExecutable(path, (mode & 0100) != 0);
689 }
690
691 /**
692 * Creates an InputStream accessing the file denoted by the path.
693 *
694 * @throws IOException if there was an error opening the file for reading
695 */
aehligc801c392017-12-19 07:12:25 -0800696 protected abstract InputStream getInputStream(Path path) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100697
698 /**
699 * Creates an OutputStream accessing the file denoted by path.
700 *
701 * @throws IOException if there was an error opening the file for writing
702 */
aehligc801c392017-12-19 07:12:25 -0800703 protected final OutputStream getOutputStream(Path path) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100704 return getOutputStream(path, false);
705 }
706
707 /**
708 * Creates an OutputStream accessing the file denoted by path.
709 *
710 * @param append whether to open the output stream in append mode
711 * @throws IOException if there was an error opening the file for writing
712 */
aehligc801c392017-12-19 07:12:25 -0800713 protected abstract OutputStream getOutputStream(Path path, boolean append) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100714
715 /**
tomluc2f6ae82017-12-08 14:19:42 -0800716 * Renames the file denoted by "sourceNode" to the location "targetNode". See {@link
717 * Path#renameTo} for specification.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100718 */
aehligc801c392017-12-19 07:12:25 -0800719 public abstract void renameTo(Path sourcePath, Path targetPath) throws IOException;
Googlere1cd9502016-09-07 14:33:29 +0000720
721 /**
722 * Create a new hard link file at "linkPath" for file at "originalPath".
723 *
724 * @param linkPath The path of the new link file to be created
725 * @param originalPath The path of the original file
726 * @throws IOException if the original file does not exist or the link file already exists
727 */
aehligc801c392017-12-19 07:12:25 -0800728 protected void createHardLink(Path linkPath, Path originalPath) throws IOException {
Googlere1cd9502016-09-07 14:33:29 +0000729
aehligc801c392017-12-19 07:12:25 -0800730 if (!originalPath.exists()) {
Googlere1cd9502016-09-07 14:33:29 +0000731 throw new FileNotFoundException(
732 "File \""
733 + originalPath.getBaseName()
734 + "\" linked from \""
735 + linkPath.getBaseName()
736 + "\" does not exist");
737 }
738
aehligc801c392017-12-19 07:12:25 -0800739 if (linkPath.exists()) {
Googlere1cd9502016-09-07 14:33:29 +0000740 throw new FileAlreadyExistsException(
741 "New link file \"" + linkPath.getBaseName() + "\" already exists");
742 }
743
744 createFSDependentHardLink(linkPath, originalPath);
745 }
746
747 /**
748 * Create a new hard link file at "linkPath" for file at "originalPath".
749 *
750 * @param linkPath The path of the new link file to be created
751 * @param originalPath The path of the original file
752 * @throws IOException if there was an I/O error
753 */
aehligc801c392017-12-19 07:12:25 -0800754 protected abstract void createFSDependentHardLink(Path linkPath, Path originalPath)
Googlere1cd9502016-09-07 14:33:29 +0000755 throws IOException;
Googlerc804c662016-12-01 16:53:28 +0000756
757 /**
aehligc801c392017-12-19 07:12:25 -0800758 * Prefetch all directories and symlinks within the package
759 * rooted at "path". Enter at most "maxDirs" total directories.
760 * Specializations for high-latency remote filesystems may wish to
Googlerc804c662016-12-01 16:53:28 +0000761 * implement this in order to warm the filesystem's internal caches.
762 */
aehligc801c392017-12-19 07:12:25 -0800763 protected void prefetchPackageAsync(Path path, int maxDirs) { }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100764}