blob: 1ba4d528f2f42f18c95dae1c51f90bb8f2643dc8 [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.
14package com.google.devtools.build.lib.skyframe;
15
tomlua155b532017-11-08 20:12:47 +010016import com.google.common.base.Preconditions;
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +000017import com.google.common.base.Predicate;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ImmutableList;
Nathan Harmataad810502015-07-29 01:33:49 +000019import com.google.common.collect.Iterables;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.Sets;
ulfjack4abd6c32017-12-21 10:52:16 -080021import com.google.devtools.build.lib.actions.FileStateType;
shahan602cc852018-06-06 20:09:57 -070022import com.google.devtools.build.lib.actions.FileStateValue;
23import com.google.devtools.build.lib.actions.FileValue;
janakre2af68f2021-03-18 15:11:30 -070024import com.google.devtools.build.lib.io.FileSymlinkCycleException;
25import com.google.devtools.build.lib.io.FileSymlinkCycleUniquenessFunction;
26import com.google.devtools.build.lib.io.FileSymlinkException;
27import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionException;
28import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionUniquenessFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
30import com.google.devtools.build.lib.util.Pair;
31import com.google.devtools.build.lib.vfs.Path;
32import com.google.devtools.build.lib.vfs.PathFragment;
33import com.google.devtools.build.lib.vfs.RootedPath;
34import com.google.devtools.build.skyframe.SkyFunction;
35import com.google.devtools.build.skyframe.SkyFunctionException;
36import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
37import com.google.devtools.build.skyframe.SkyKey;
Googler54e24df2016-03-28 19:11:39 +000038import java.io.IOException;
Nathan Harmataad810502015-07-29 01:33:49 +000039import java.util.ArrayList;
40import java.util.TreeSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010041import java.util.concurrent.atomic.AtomicReference;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import javax.annotation.Nullable;
43
44/**
45 * A {@link SkyFunction} for {@link FileValue}s.
46 *
nharmata4ec695c2019-02-19 08:30:22 -080047 * <p>Most of the complexity in the implementation results from wanting incremental correctness in
48 * the presence of symlinks, esp. ancestor directory symlinks.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049 */
50public class FileFunction implements SkyFunction {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051 private final AtomicReference<PathPackageLocator> pkgLocator;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000053 public FileFunction(AtomicReference<PathPackageLocator> pkgLocator) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054 this.pkgLocator = pkgLocator;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055 }
56
lberki7598bc62020-10-02 01:33:14 -070057 private static class SymlinkResolutionState {
nharmata4ec695c2019-02-19 08:30:22 -080058 // Suppose we have a path p. One of the goals of FileFunction is to resolve the "real path", if
59 // any, of p. The basic algorithm is to use the fully resolved path of p's parent directory to
60 // determine the fully resolved path of p. This is complicated when symlinks are involved, and
61 // is especially complicated when ancestor directory symlinks are involved.
62 //
63 // Since FileStateValues are the roots of invalidation, care has to be taken to ensuring we
64 // declare the proper FileStateValue deps. As a concrete example, let p = a/b and imagine (i) a
65 // is a direct symlink to c and also (ii) c/b is an existing file. Among other direct deps, we
66 // want to have a direct dep on FileStateValue(c/b), since that's the node that will be changed
67 // if the actual contents of a/b (aka c/b) changes. To rephrase: a dep on FileStateValue(a/b)
68 // won't do anything productive since that path will never be in the Skyframe diff.
69 //
70 // In the course of resolving the real path of p, there will be a logical chain of paths we
71 // consider. Going with the example from above, the full chain of paths we consider is
72 // [a/b, c/b].
73 ArrayList<RootedPath> logicalChain = new ArrayList<>();
74 // Same contents as 'logicalChain', except stored as an sorted TreeSet for efficiency reasons.
75 // See the usage in checkPathSeenDuringPartialResolutionInternal.
76 TreeSet<Path> sortedLogicalChain = Sets.newTreeSet();
77
lberki7598bc62020-10-02 01:33:14 -070078 ImmutableList<RootedPath> pathToUnboundedAncestorSymlinkExpansionChain = null;
79 ImmutableList<RootedPath> unboundedAncestorSymlinkExpansionChain = null;
80
81 private SymlinkResolutionState() {}
82 }
83
84 @Override
85 public FileValue compute(SkyKey skyKey, Environment env)
86 throws FileFunctionException, InterruptedException {
87 RootedPath rootedPath = (RootedPath) skyKey.argument();
88 SymlinkResolutionState symlinkResolutionState = new SymlinkResolutionState();
89
nharmata4ec695c2019-02-19 08:30:22 -080090 // Fully resolve the path of the parent directory, but only if the current file is not the
91 // filesystem root (has no parent) or a package path root (treated opaquely and handled by
92 // skyframe's DiffAwareness interface).
93 //
94 // This entails resolving ancestor symlinks fully. Note that this is the first thing we do - if
95 // an ancestor is part of a symlink cycle, we want to detect that quickly as it gives a more
96 // informative error message than we'd get doing bogus filesystem operations.
97 PartialResolutionResult resolveFromAncestorsResult =
lberki7598bc62020-10-02 01:33:14 -070098 resolveFromAncestors(rootedPath, symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -080099 if (resolveFromAncestorsResult == null) {
100 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101 }
nharmata4ec695c2019-02-19 08:30:22 -0800102 RootedPath rootedPathFromAncestors = resolveFromAncestorsResult.rootedPath;
103 FileStateValue fileStateValueFromAncestors = resolveFromAncestorsResult.fileStateValue;
104 if (fileStateValueFromAncestors.getType() == FileStateType.NONEXISTENT) {
105 return FileValue.value(
lberki7598bc62020-10-02 01:33:14 -0700106 ImmutableList.copyOf(symlinkResolutionState.logicalChain),
107 symlinkResolutionState.pathToUnboundedAncestorSymlinkExpansionChain,
108 symlinkResolutionState.unboundedAncestorSymlinkExpansionChain,
nharmata4ec695c2019-02-19 08:30:22 -0800109 rootedPath,
110 FileStateValue.NONEXISTENT_FILE_STATE_NODE,
111 rootedPathFromAncestors,
112 fileStateValueFromAncestors);
113 }
114
115 RootedPath realRootedPath = rootedPathFromAncestors;
116 FileStateValue realFileStateValue = fileStateValueFromAncestors;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100117
ulfjack4abd6c32017-12-21 10:52:16 -0800118 while (realFileStateValue.getType().isSymlink()) {
nharmata4ec695c2019-02-19 08:30:22 -0800119 PartialResolutionResult getSymlinkTargetRootedPathResult =
120 getSymlinkTargetRootedPath(
lberki7598bc62020-10-02 01:33:14 -0700121 realRootedPath, realFileStateValue.getSymlinkTarget(), symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -0800122 if (getSymlinkTargetRootedPathResult == null) {
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000123 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100124 }
nharmata4ec695c2019-02-19 08:30:22 -0800125 realRootedPath = getSymlinkTargetRootedPathResult.rootedPath;
126 realFileStateValue = getSymlinkTargetRootedPathResult.fileStateValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127 }
nharmata4ec695c2019-02-19 08:30:22 -0800128
129 return FileValue.value(
lberki7598bc62020-10-02 01:33:14 -0700130 ImmutableList.copyOf(symlinkResolutionState.logicalChain),
131 symlinkResolutionState.pathToUnboundedAncestorSymlinkExpansionChain,
132 symlinkResolutionState.unboundedAncestorSymlinkExpansionChain,
nharmata4ec695c2019-02-19 08:30:22 -0800133 rootedPath,
nharmatab07cd062019-02-19 09:52:50 -0800134 fileStateValueFromAncestors,
nharmata4ec695c2019-02-19 08:30:22 -0800135 realRootedPath,
136 realFileStateValue);
137 }
138
nharmata4ec695c2019-02-19 08:30:22 -0800139 private static RootedPath getChild(RootedPath parentRootedPath, String baseName) {
140 return RootedPath.toRootedPath(
141 parentRootedPath.getRoot(), parentRootedPath.getRootRelativePath().getChild(baseName));
142 }
143
144 private RootedPath toRootedPath(Path path) {
145 return RootedPath.toRootedPathMaybeUnderRoot(path, pkgLocator.get().getPathEntries());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100146 }
147
148 /**
149 * Returns the path and file state of {@code rootedPath}, accounting for ancestor symlinks, or
150 * {@code null} if there was a missing dep.
151 */
152 @Nullable
jhorvitz85146042021-04-29 10:12:10 -0700153 private static PartialResolutionResult resolveFromAncestors(
lberki7598bc62020-10-02 01:33:14 -0700154 RootedPath rootedPath, SymlinkResolutionState symlinkResolutionState, Environment env)
nharmata4ec695c2019-02-19 08:30:22 -0800155 throws InterruptedException, FileFunctionException {
ajurkowski75172892019-11-04 09:35:40 -0800156 RootedPath parentRootedPath = rootedPath.getParentDirectory();
157 return parentRootedPath != null
lberki7598bc62020-10-02 01:33:14 -0700158 ? resolveFromAncestorsWithParent(rootedPath, parentRootedPath, symlinkResolutionState, env)
159 : resolveFromAncestorsNoParent(rootedPath, symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -0800160 }
Googler54e24df2016-03-28 19:11:39 +0000161
nharmata4ec695c2019-02-19 08:30:22 -0800162 @Nullable
jhorvitz85146042021-04-29 10:12:10 -0700163 private static PartialResolutionResult resolveFromAncestorsWithParent(
nharmata4ec695c2019-02-19 08:30:22 -0800164 RootedPath rootedPath,
ajurkowski75172892019-11-04 09:35:40 -0800165 RootedPath parentRootedPath,
lberki7598bc62020-10-02 01:33:14 -0700166 SymlinkResolutionState symlinkResolutionState,
nharmata4ec695c2019-02-19 08:30:22 -0800167 Environment env)
168 throws InterruptedException, FileFunctionException {
169 PathFragment relativePath = rootedPath.getRootRelativePath();
170 RootedPath rootedPathFromAncestors;
171 String baseName = relativePath.getBaseName();
nharmata4ec695c2019-02-19 08:30:22 -0800172
173 FileValue parentFileValue = (FileValue) env.getValue(FileValue.key(parentRootedPath));
174 if (parentFileValue == null) {
175 return null;
176 }
177 rootedPathFromAncestors = getChild(parentFileValue.realRootedPath(), baseName);
178
179 if (!parentFileValue.exists() || !parentFileValue.isDirectory()) {
nharmata4ec695c2019-02-19 08:30:22 -0800180 return new PartialResolutionResult(
181 rootedPathFromAncestors, FileStateValue.NONEXISTENT_FILE_STATE_NODE);
182 }
183
184 for (RootedPath parentPartialRootedPath : parentFileValue.logicalChainDuringResolution()) {
185 checkAndNotePathSeenDuringPartialResolution(
lberki7598bc62020-10-02 01:33:14 -0700186 getChild(parentPartialRootedPath, baseName), symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -0800187 if (env.valuesMissing()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100188 return null;
189 }
nharmata4ec695c2019-02-19 08:30:22 -0800190 }
Googler54e24df2016-03-28 19:11:39 +0000191
nharmata4ec695c2019-02-19 08:30:22 -0800192 FileStateValue fileStateValueFromAncestors =
193 (FileStateValue) env.getValue(FileStateValue.key(rootedPathFromAncestors));
194 if (fileStateValueFromAncestors == null) {
195 return null;
196 }
197
198 return new PartialResolutionResult(rootedPathFromAncestors, fileStateValueFromAncestors);
199 }
200
201 @Nullable
jhorvitz85146042021-04-29 10:12:10 -0700202 private static PartialResolutionResult resolveFromAncestorsNoParent(
lberki7598bc62020-10-02 01:33:14 -0700203 RootedPath rootedPath, SymlinkResolutionState symlinkResolutionState, Environment env)
nharmata4ec695c2019-02-19 08:30:22 -0800204 throws InterruptedException, FileFunctionException {
lberki7598bc62020-10-02 01:33:14 -0700205 checkAndNotePathSeenDuringPartialResolution(rootedPath, symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -0800206 if (env.valuesMissing()) {
207 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100208 }
209 FileStateValue realFileStateValue =
nharmata4ec695c2019-02-19 08:30:22 -0800210 (FileStateValue) env.getValue(FileStateValue.key(rootedPath));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 if (realFileStateValue == null) {
212 return null;
213 }
nharmata4ec695c2019-02-19 08:30:22 -0800214 return new PartialResolutionResult(rootedPath, realFileStateValue);
215 }
216
217 private static final class PartialResolutionResult {
218 private final RootedPath rootedPath;
219 private final FileStateValue fileStateValue;
220
221 private PartialResolutionResult(RootedPath rootedPath, FileStateValue fileStateValue) {
222 this.rootedPath = rootedPath;
223 this.fileStateValue = fileStateValue;
224 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100225 }
226
227 /**
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000228 * Returns the symlink target and file state of {@code rootedPath}'s symlink to {@code
229 * symlinkTarget}, accounting for ancestor symlinks, or {@code null} if there was a missing dep.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100230 */
231 @Nullable
nharmata4ec695c2019-02-19 08:30:22 -0800232 private PartialResolutionResult getSymlinkTargetRootedPath(
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000233 RootedPath rootedPath,
234 PathFragment symlinkTarget,
lberki7598bc62020-10-02 01:33:14 -0700235 SymlinkResolutionState symlinkResolutionState,
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000236 Environment env)
237 throws FileFunctionException, InterruptedException {
nharmata4ec695c2019-02-19 08:30:22 -0800238 Path path = rootedPath.asPath();
239 Path symlinkTargetPath;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100240 if (symlinkTarget.isAbsolute()) {
nharmata4ec695c2019-02-19 08:30:22 -0800241 symlinkTargetPath = path.getRelative(symlinkTarget);
Nathan Harmataad810502015-07-29 01:33:49 +0000242 } else {
nharmata4ec695c2019-02-19 08:30:22 -0800243 Path parentPath = path.getParentDirectory();
244 symlinkTargetPath =
245 parentPath != null
246 ? parentPath.getRelative(symlinkTarget)
247 : path.getRelative(symlinkTarget);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100248 }
nharmata4ec695c2019-02-19 08:30:22 -0800249 RootedPath symlinkTargetRootedPath = toRootedPath(symlinkTargetPath);
lberki7598bc62020-10-02 01:33:14 -0700250 checkPathSeenDuringPartialResolution(symlinkTargetRootedPath, symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -0800251 if (env.valuesMissing()) {
252 return null;
253 }
254 // The symlink target could have a different parent directory, which itself could be a directory
255 // symlink (or have an ancestor directory symlink)!
lberki7598bc62020-10-02 01:33:14 -0700256 return resolveFromAncestors(symlinkTargetRootedPath, symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -0800257 }
258
jhorvitz85146042021-04-29 10:12:10 -0700259 private static void checkAndNotePathSeenDuringPartialResolution(
lberki7598bc62020-10-02 01:33:14 -0700260 RootedPath rootedPath, SymlinkResolutionState symlinkResolutionState, Environment env)
nharmata4ec695c2019-02-19 08:30:22 -0800261 throws FileFunctionException, InterruptedException {
262 Path path = rootedPath.asPath();
lberki7598bc62020-10-02 01:33:14 -0700263 checkPathSeenDuringPartialResolutionInternal(rootedPath, path, symlinkResolutionState, env);
264 symlinkResolutionState.sortedLogicalChain.add(path);
265 symlinkResolutionState.logicalChain.add(rootedPath);
nharmata4ec695c2019-02-19 08:30:22 -0800266 }
267
jhorvitz85146042021-04-29 10:12:10 -0700268 private static void checkPathSeenDuringPartialResolution(
lberki7598bc62020-10-02 01:33:14 -0700269 RootedPath rootedPath, SymlinkResolutionState symlinkResolutionState, Environment env)
nharmata4ec695c2019-02-19 08:30:22 -0800270 throws FileFunctionException, InterruptedException {
271 checkPathSeenDuringPartialResolutionInternal(
lberki7598bc62020-10-02 01:33:14 -0700272 rootedPath, rootedPath.asPath(), symlinkResolutionState, env);
nharmata4ec695c2019-02-19 08:30:22 -0800273 }
274
jhorvitz85146042021-04-29 10:12:10 -0700275 private static void checkPathSeenDuringPartialResolutionInternal(
nharmata4ec695c2019-02-19 08:30:22 -0800276 RootedPath rootedPath,
277 Path path,
lberki7598bc62020-10-02 01:33:14 -0700278 SymlinkResolutionState symlinkResolutionState,
nharmata4ec695c2019-02-19 08:30:22 -0800279 Environment env)
280 throws FileFunctionException, InterruptedException {
281 // We are about to perform another step of partial real path resolution. 'logicalChain' is the
282 // chain of paths we've considered so far, and 'rootedPath' / 'path' is the proposed next path
283 // we consider.
Shreya Bhattaraif87a4142015-10-09 19:48:11 +0000284 //
nharmata4ec695c2019-02-19 08:30:22 -0800285 // Before we proceed with 'rootedPath', we need to ensure there won't be a problem. There are
286 // three sorts of issues, all stemming from symlinks:
287 // (i) Symlink cycle:
288 // p -> p1 -> p2 -> p1
289 // (ii) Unbounded expansion caused by a symlink to a descendant of a member of the chain:
290 // p -> a/b -> c/d -> a/b/e
291 // (iii) Unbounded expansion caused by a symlink to an ancestor of a member of the chain:
292 // p -> a/b -> c/d -> a
Nathan Harmata808217c2015-10-12 22:07:19 +0000293 //
nharmata4ba404f2019-08-23 15:39:44 -0700294 // We can detect all three of these symlink issues via inspection of the proposed new element.
295 // Here is our incremental algorithm:
nharmata4ec695c2019-02-19 08:30:22 -0800296 // If 'path' is in 'sortedLogicalChain' then we have a found a cycle (i).
297 // If 'path' is a descendant of any path p in 'sortedLogicalChain' then we have unbounded
298 // expansion (ii).
299 // If 'path' is an ancestor of any path p in 'sortedLogicalChain' then we have unbounded
300 // expansion (iii).
Nathan Harmata808217c2015-10-12 22:07:19 +0000301 // We can check for these cases efficiently (read: sublinear time) by finding the extremal
nharmata4ec695c2019-02-19 08:30:22 -0800302 // candidate p for (ii) and (iii).
Nathan Harmata808217c2015-10-12 22:07:19 +0000303 SkyKey uniquenessKey = null;
304 FileSymlinkException fse = null;
lberki7598bc62020-10-02 01:33:14 -0700305 Path seenFloorPath = symlinkResolutionState.sortedLogicalChain.floor(path);
306 Path seenCeilingPath = symlinkResolutionState.sortedLogicalChain.ceiling(path);
307 if (symlinkResolutionState.sortedLogicalChain.contains(path)) {
nharmata4ec695c2019-02-19 08:30:22 -0800308 // 'rootedPath' is [transitively] a symlink to a previous element in the symlink chain (i).
Nathan Harmata808217c2015-10-12 22:07:19 +0000309 Pair<ImmutableList<RootedPath>, ImmutableList<RootedPath>> pathAndChain =
lberki7598bc62020-10-02 01:33:14 -0700310 CycleUtils.splitIntoPathAndChain(
311 isPathPredicate(path), symlinkResolutionState.logicalChain);
Nathan Harmata808217c2015-10-12 22:07:19 +0000312 FileSymlinkCycleException fsce =
313 new FileSymlinkCycleException(pathAndChain.getFirst(), pathAndChain.getSecond());
314 uniquenessKey = FileSymlinkCycleUniquenessFunction.key(fsce.getCycle());
315 fse = fsce;
nharmata4ec695c2019-02-19 08:30:22 -0800316 } else if (seenFloorPath != null && path.startsWith(seenFloorPath)) {
317 // 'rootedPath' is [transitively] a symlink to a descendant of a previous element in the
318 // symlink chain (ii).
Nathan Harmata808217c2015-10-12 22:07:19 +0000319 Pair<ImmutableList<RootedPath>, ImmutableList<RootedPath>> pathAndChain =
320 CycleUtils.splitIntoPathAndChain(
321 isPathPredicate(seenFloorPath),
lberki7598bc62020-10-02 01:33:14 -0700322 ImmutableList.copyOf(
323 Iterables.concat(
324 symlinkResolutionState.logicalChain, ImmutableList.of(rootedPath))));
Nathan Harmata808217c2015-10-12 22:07:19 +0000325 uniquenessKey = FileSymlinkInfiniteExpansionUniquenessFunction.key(pathAndChain.getSecond());
jhorvitz85146042021-04-29 10:12:10 -0700326 fse =
327 new FileSymlinkInfiniteExpansionException(
328 pathAndChain.getFirst(), pathAndChain.getSecond());
nharmata4ec695c2019-02-19 08:30:22 -0800329 } else if (seenCeilingPath != null && seenCeilingPath.startsWith(path)) {
330 // 'rootedPath' is [transitively] a symlink to an ancestor of a previous element in the
331 // symlink chain (iii).
lberki7598bc62020-10-02 01:33:14 -0700332 if (symlinkResolutionState.unboundedAncestorSymlinkExpansionChain == null) {
333 Pair<ImmutableList<RootedPath>, ImmutableList<RootedPath>> pathAndChain =
334 CycleUtils.splitIntoPathAndChain(
335 isPathPredicate(seenCeilingPath),
336 ImmutableList.copyOf(
337 Iterables.concat(
338 symlinkResolutionState.logicalChain, ImmutableList.of(rootedPath))));
339 symlinkResolutionState.pathToUnboundedAncestorSymlinkExpansionChain =
340 pathAndChain.getFirst();
341 symlinkResolutionState.unboundedAncestorSymlinkExpansionChain = pathAndChain.getSecond();
342 }
Nathan Harmata808217c2015-10-12 22:07:19 +0000343 }
344 if (uniquenessKey != null) {
nharmata4ec695c2019-02-19 08:30:22 -0800345 // Note that this dependency is merely to ensure that each unique symlink error gets
346 // reported exactly once.
347 env.getValue(uniquenessKey);
348 if (env.valuesMissing()) {
349 return;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100350 }
ulfjacke4794532018-01-12 02:11:17 -0800351 throw new FileFunctionException(
352 Preconditions.checkNotNull(fse, rootedPath), Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100353 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100354 }
355
jhorvitz85146042021-04-29 10:12:10 -0700356 private static Predicate<RootedPath> isPathPredicate(Path path) {
357 return rootedPath -> rootedPath.asPath().equals(path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100358 }
359
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100360 /**
jhorvitz85146042021-04-29 10:12:10 -0700361 * Used to declare all the exception types that can be wrapped in the exception thrown by {@link
362 * FileFunction#compute}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100363 */
364 private static final class FileFunctionException extends SkyFunctionException {
jhorvitz85146042021-04-29 10:12:10 -0700365 FileFunctionException(IOException e, Transience transience) {
Googler54e24df2016-03-28 19:11:39 +0000366 super(e, transience);
367 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100368 }
369}