blob: 42e0cd90b5bf5179635f4cc4b6382877b24f6a87 [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
Googler8b68efe2023-03-15 14:57:50 -070016import static com.google.common.base.Preconditions.checkArgument;
17import static com.google.common.base.Preconditions.checkNotNull;
fellyf1e30f32019-07-09 12:26:10 -070018import static java.nio.charset.StandardCharsets.UTF_8;
19
Googler5284e6c2019-10-30 07:43:54 -070020import com.google.common.annotations.VisibleForTesting;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.common.base.Verify;
Googlerc8acdbd2018-11-08 10:47:15 -080022import com.google.common.collect.ImmutableList;
felly5be4dd62018-02-05 11:11:53 -080023import com.google.devtools.build.lib.actions.Artifact;
Googlerc8acdbd2018-11-08 10:47:15 -080024import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
shahan602cc852018-06-06 20:09:57 -070025import com.google.devtools.build.lib.actions.FileArtifactValue;
fellyf1e30f32019-07-09 12:26:10 -070026import com.google.devtools.build.lib.actions.FileStateType;
27import com.google.devtools.build.lib.actions.FileStateValue;
Googler8b68efe2023-03-15 14:57:50 -070028import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValueWithContentsProxy;
29import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValueWithDigest;
shahan602cc852018-06-06 20:09:57 -070030import com.google.devtools.build.lib.actions.FileValue;
fellyf1e30f32019-07-09 12:26:10 -070031import com.google.devtools.build.lib.actions.HasDigest;
Laszlo Csomor207140f2015-12-07 15:07:33 +000032import com.google.devtools.build.lib.collect.nestedset.NestedSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
34import com.google.devtools.build.lib.events.Event;
janakre2af68f2021-03-18 15:11:30 -070035import com.google.devtools.build.lib.io.FileSymlinkException;
36import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionException;
37import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionUniquenessFunction;
Googler0d4739e2023-10-30 10:00:55 -070038import com.google.devtools.build.lib.io.InconsistentFilesystemException;
janakrb77246c2021-03-05 14:41:58 -080039import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
twerth45d82ca2021-02-24 02:28:54 -080040import com.google.devtools.build.lib.profiler.Profiler;
41import com.google.devtools.build.lib.profiler.ProfilerTask;
42import com.google.devtools.build.lib.profiler.SilentCloseable;
janakra4a564a2021-03-18 12:40:11 -070043import com.google.devtools.build.lib.server.FailureDetails;
jcaterb11c6e22020-04-03 11:40:16 -070044import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.FileType;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFile;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +000046import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactory;
Googlerde9d1f52023-11-06 09:28:56 -080047import com.google.devtools.build.lib.skyframe.serialization.VisibleForSerialization;
Googler0129a242021-12-01 09:04:45 -080048import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
fellyf1e30f32019-07-09 12:26:10 -070049import com.google.devtools.build.lib.util.Fingerprint;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050import com.google.devtools.build.lib.vfs.Dirent;
felly5be4dd62018-02-05 11:11:53 -080051import com.google.devtools.build.lib.vfs.FileStatus;
felly5be4dd62018-02-05 11:11:53 -080052import com.google.devtools.build.lib.vfs.Path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080054import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055import com.google.devtools.build.lib.vfs.RootedPath;
felly5be4dd62018-02-05 11:11:53 -080056import com.google.devtools.build.lib.vfs.Symlinks;
janakrfc1d79c2022-01-27 13:02:07 -080057import com.google.devtools.build.lib.vfs.SyscallCache;
janakre2853222022-03-08 10:52:18 -080058import com.google.devtools.build.lib.vfs.XattrProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010059import com.google.devtools.build.skyframe.SkyFunction;
60import com.google.devtools.build.skyframe.SkyFunctionException;
61import com.google.devtools.build.skyframe.SkyKey;
62import com.google.devtools.build.skyframe.SkyValue;
Googlerb5052b82022-11-03 11:33:56 -070063import com.google.devtools.build.skyframe.SkyframeLookupResult;
kush95bf7c82017-08-30 00:27:35 +020064import java.io.IOException;
fellyf1e30f32019-07-09 12:26:10 -070065import java.math.BigInteger;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010066import java.util.ArrayList;
Googlerd78fb572022-07-06 11:44:42 -070067import java.util.Collection;
felly5be4dd62018-02-05 11:11:53 -080068import java.util.Collections;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069import java.util.List;
70import java.util.Map;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071import javax.annotation.Nullable;
72
73/** A {@link SkyFunction} to build {@link RecursiveFilesystemTraversalValue}s. */
74public final class RecursiveFilesystemTraversalFunction implements SkyFunction {
fellyf1e30f32019-07-09 12:26:10 -070075 private static final byte[] MISSING_FINGERPRINT =
76 new BigInteger(1, "NonexistentFileStateValue".getBytes(UTF_8)).toByteArray();
77
Googlerde9d1f52023-11-06 09:28:56 -080078 @SerializationConstant @VisibleForSerialization
fellyf1e30f32019-07-09 12:26:10 -070079 static final HasDigest NON_EXISTENT_HAS_DIGEST = () -> MISSING_FINGERPRINT;
80
81 private static final FileInfo NON_EXISTENT_FILE_INFO =
82 new FileInfo(FileType.NONEXISTENT, NON_EXISTENT_HAS_DIGEST, null, null);
83
mschallerc12c0442020-06-08 12:06:35 -070084 /** The exception that {@link RecursiveFilesystemTraversalFunctionException} wraps. */
85 public static class RecursiveFilesystemTraversalException extends Exception {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086
mschallerc12c0442020-06-08 12:06:35 -070087 /**
88 * Categories of errors that prevent normal {@link RecursiveFilesystemTraversalFunction}
89 * evaluation.
90 */
91 public enum Type {
92 /**
93 * The traversal encountered a subdirectory with a BUILD file but is not allowed to recurse
94 * into it. See {@code PackageBoundaryMode#REPORT_ERROR}.
95 */
96 CANNOT_CROSS_PACKAGE_BOUNDARY,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097
mschallerc12c0442020-06-08 12:06:35 -070098 /** A dangling symlink was dereferenced. */
99 DANGLING_SYMLINK,
100
101 /** A file operation failed. */
102 FILE_OPERATION_FAILURE,
103
104 /** A generated directory's root-relative path conflicts with a package's path. */
105 GENERATED_PATH_CONFLICT,
janakrb77246c2021-03-05 14:41:58 -0800106
107 /** A file/directory visited was part of a symlink cycle or infinite expansion. */
108 SYMLINK_CYCLE_OR_INFINITE_EXPANSION,
Googler0d4739e2023-10-30 10:00:55 -0700109
110 /** The filesystem told us inconsistent information. */
111 INCONSISTENT_FILESYSTEM,
mschallerc12c0442020-06-08 12:06:35 -0700112 }
113
Googler8b68efe2023-03-15 14:57:50 -0700114 private final RecursiveFilesystemTraversalException.Type type;
mschallerc12c0442020-06-08 12:06:35 -0700115
Googler8b68efe2023-03-15 14:57:50 -0700116 RecursiveFilesystemTraversalException(
117 String message, RecursiveFilesystemTraversalException.Type type) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 super(message);
mschallerc12c0442020-06-08 12:06:35 -0700119 this.type = type;
120 }
121
Googler8b68efe2023-03-15 14:57:50 -0700122 public RecursiveFilesystemTraversalException.Type getType() {
mschallerc12c0442020-06-08 12:06:35 -0700123 return type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100124 }
125 }
126
127 /**
128 * Thrown when a dangling symlink is attempted to be dereferenced.
129 *
130 * <p>Note: this class is not identical to the one in com.google.devtools.build.lib.view.fileset
131 * and it's not easy to merge the two because of the dependency structure. The other one will
132 * probably be removed along with the rest of the legacy Fileset code.
133 */
mschallerc12c0442020-06-08 12:06:35 -0700134 static final class DanglingSymlinkException extends RecursiveFilesystemTraversalException {
135 DanglingSymlinkException(String path, String unresolvedLink) {
Laszlo Csomor0ad729f2015-12-02 15:20:35 +0000136 super(
137 String.format(
mschallerc12c0442020-06-08 12:06:35 -0700138 "Found dangling symlink: %s, unresolved path: \"%s\"", path, unresolvedLink),
Googler8b68efe2023-03-15 14:57:50 -0700139 RecursiveFilesystemTraversalException.Type.DANGLING_SYMLINK);
140 checkArgument(path != null && !path.isEmpty());
141 checkArgument(unresolvedLink != null && !unresolvedLink.isEmpty());
kush95bf7c82017-08-30 00:27:35 +0200142 }
143 }
144
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100145 /** Exception type thrown by {@link RecursiveFilesystemTraversalFunction#compute}. */
146 private static final class RecursiveFilesystemTraversalFunctionException extends
147 SkyFunctionException {
148 RecursiveFilesystemTraversalFunctionException(RecursiveFilesystemTraversalException e) {
149 super(e, Transience.PERSISTENT);
150 }
151 }
152
janakrb2a94342022-02-05 22:02:14 -0800153 private final SyscallCache syscallCache;
janakr491e4412022-01-28 15:35:34 -0800154
janakrb2a94342022-02-05 22:02:14 -0800155 RecursiveFilesystemTraversalFunction(SyscallCache syscallCache) {
janakr491e4412022-01-28 15:35:34 -0800156 this.syscallCache = syscallCache;
157 }
158
janakrb77246c2021-03-05 14:41:58 -0800159 @Nullable
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100160 @Override
Googlere0ba7962022-08-11 08:37:36 -0700161 public RecursiveFilesystemTraversalValue compute(SkyKey skyKey, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000162 throws RecursiveFilesystemTraversalFunctionException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100163 TraversalRequest traversal = (TraversalRequest) skyKey.argument();
twerth45d82ca2021-02-24 02:28:54 -0800164 try (SilentCloseable c =
165 Profiler.instance()
Googlerd78fb572022-07-06 11:44:42 -0700166 .profile(ProfilerTask.FILESYSTEM_TRAVERSAL, traversal.root().toString())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100167 // Stat the traversal root.
janakrb2a94342022-02-05 22:02:14 -0800168 FileInfo rootInfo = lookUpFileInfo(env, traversal, syscallCache);
janakrb77246c2021-03-05 14:41:58 -0800169 if (rootInfo == null) {
170 return null;
171 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100172
173 if (!rootInfo.type.exists()) {
174 // May be a dangling symlink or a non-existent file. Handle gracefully.
175 if (rootInfo.type.isSymlink()) {
Googler0d4739e2023-10-30 10:00:55 -0700176 return RecursiveFilesystemTraversalValue.of(
177 ResolvedFileFactory.danglingSymlink(
178 traversal.root().asRootedPath(),
179 rootInfo.unresolvedSymlinkTarget,
180 rootInfo.metadata));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100181 } else {
182 return RecursiveFilesystemTraversalValue.EMPTY;
183 }
184 }
185
186 if (rootInfo.type.isFile()) {
Googlerd78fb572022-07-06 11:44:42 -0700187 return resultForFileRoot(traversal.root().asRootedPath(), rootInfo);
188 }
Googler6f48f1c2024-04-16 14:29:09 -0700189 if (rootInfo.type.isDirectory() && rootInfo.metadata instanceof TreeArtifactValue value) {
Googlerd78fb572022-07-06 11:44:42 -0700190 ImmutableList.Builder<RecursiveFilesystemTraversalValue> traversalValues =
191 ImmutableList.builderWithExpectedSize(value.getChildValues().size());
Googlerc8acdbd2018-11-08 10:47:15 -0800192 for (Map.Entry<TreeFileArtifact, FileArtifactValue> entry
193 : value.getChildValues().entrySet()) {
194 RootedPath path =
Googlerd78fb572022-07-06 11:44:42 -0700195 RootedPath.toRootedPath(traversal.root().getRootPart(), entry.getKey().getPath());
196 traversalValues.add(
jcaterb11c6e22020-04-03 11:40:16 -0700197 resultForFileRoot(
198 path,
199 // TreeArtifact can't have symbolic inside. So the assumption for FileType.FILE
200 // is always true.
201 new FileInfo(FileType.FILE, entry.getValue(), path, null)));
Googlerc8acdbd2018-11-08 10:47:15 -0800202 }
Googlerd78fb572022-07-06 11:44:42 -0700203 return resultForDirectory(traversal, rootInfo, traversalValues.build());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100204 }
205
206 // Otherwise the root is a directory or a symlink to one.
janakrb2a94342022-02-05 22:02:14 -0800207 PkgLookupResult pkgLookupResult = checkIfPackage(env, traversal, rootInfo, syscallCache);
janakrb77246c2021-03-05 14:41:58 -0800208 if (pkgLookupResult == null) {
209 return null;
210 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 traversal = pkgLookupResult.traversal;
212
213 if (pkgLookupResult.isConflicting()) {
214 // The traversal was requested for an output directory whose root-relative path conflicts
215 // with a source package. We can't handle that, bail out.
mschallerc12c0442020-06-08 12:06:35 -0700216 throw createGeneratedPathConflictException(traversal);
Googlerd78fb572022-07-06 11:44:42 -0700217 } else if (pkgLookupResult.isPackage() && !traversal.skipTestingForSubpackage()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100218 // The traversal was requested for a directory that defines a package.
tomlu8cc5dcf2018-01-19 09:28:06 -0800219 String msg =
Googlerd78fb572022-07-06 11:44:42 -0700220 traversal.errorInfo()
tomlu8cc5dcf2018-01-19 09:28:06 -0800221 + " crosses package boundary into package rooted at "
Googlerd78fb572022-07-06 11:44:42 -0700222 + traversal.root().getRelativePart().getPathString();
223 switch (traversal.crossPkgBoundaries()) {
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000224 case CROSS:
225 // We are free to traverse the subpackage but we need to display a warning.
Ulf Adams760e7092016-04-21 08:09:51 +0000226 env.getListener().handle(Event.warn(null, msg));
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000227 break;
228 case DONT_CROSS:
229 // We cannot traverse the subpackage and should skip it silently. Return empty results.
230 return RecursiveFilesystemTraversalValue.EMPTY;
231 case REPORT_ERROR:
232 // We cannot traverse the subpackage and should complain loudly (display an error).
233 throw new RecursiveFilesystemTraversalFunctionException(
mschallerc12c0442020-06-08 12:06:35 -0700234 new RecursiveFilesystemTraversalException(
235 msg, RecursiveFilesystemTraversalException.Type.CANNOT_CROSS_PACKAGE_BOUNDARY));
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000236 default:
237 throw new IllegalStateException(traversal.toString());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100238 }
239 }
240
241 // We are free to traverse this directory.
emilyguo305fcc32022-03-29 10:10:07 -0700242 ImmutableList<RecursiveFilesystemTraversalValue> subdirTraversals =
emilyguoc07e66d2022-03-23 18:57:24 -0700243 traverseChildren(env, traversal);
janakrb77246c2021-03-05 14:41:58 -0800244 if (subdirTraversals == null) {
245 return null;
246 }
247 return resultForDirectory(traversal, rootInfo, subdirTraversals);
248 } catch (IOException | BuildFileNotFoundException e) {
249 String message =
250 String.format(
251 "Error while traversing directory %s: %s",
Googlerd78fb572022-07-06 11:44:42 -0700252 traversal.root().getRelativePart(), e.getMessage());
janakra4a564a2021-03-18 12:40:11 -0700253 // Trying to stat the starting point of this root may have failed with a symlink cycle or
254 // trying to get a package lookup value may have failed due to a symlink cycle.
255 RecursiveFilesystemTraversalException.Type exceptionType =
256 RecursiveFilesystemTraversalException.Type.FILE_OPERATION_FAILURE;
Googler0d4739e2023-10-30 10:00:55 -0700257 if (e instanceof InconsistentFilesystemException) {
258 exceptionType = RecursiveFilesystemTraversalException.Type.INCONSISTENT_FILESYSTEM;
259 }
janakra4a564a2021-03-18 12:40:11 -0700260 if (e instanceof FileSymlinkException) {
261 exceptionType =
262 RecursiveFilesystemTraversalException.Type.SYMLINK_CYCLE_OR_INFINITE_EXPANSION;
263 }
264 if (e instanceof DetailedException) {
265 FailureDetails.PackageLoading.Code code =
266 ((DetailedException) e)
267 .getDetailedExitCode()
268 .getFailureDetail()
269 .getPackageLoading()
270 .getCode();
271 if (code == FailureDetails.PackageLoading.Code.SYMLINK_CYCLE_OR_INFINITE_EXPANSION) {
272 exceptionType =
273 RecursiveFilesystemTraversalException.Type.SYMLINK_CYCLE_OR_INFINITE_EXPANSION;
274 }
275 }
kush95bf7c82017-08-30 00:27:35 +0200276 throw new RecursiveFilesystemTraversalFunctionException(
janakra4a564a2021-03-18 12:40:11 -0700277 new RecursiveFilesystemTraversalException(message, exceptionType));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100278 }
279 }
280
mschallerc12c0442020-06-08 12:06:35 -0700281 private static RecursiveFilesystemTraversalFunctionException createGeneratedPathConflictException(
282 TraversalRequest traversal) {
283 String message =
284 String.format(
285 "Generated directory %s conflicts with package under the same path. "
286 + "Additional info: %s",
Googlerd78fb572022-07-06 11:44:42 -0700287 traversal.root().getRelativePart().getPathString(), traversal.errorInfo());
mschallerc12c0442020-06-08 12:06:35 -0700288 return new RecursiveFilesystemTraversalFunctionException(
289 new RecursiveFilesystemTraversalException(
290 message, RecursiveFilesystemTraversalException.Type.GENERATED_PATH_CONFLICT));
291 }
292
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100293 private static final class FileInfo {
294 final FileType type;
fellyf1e30f32019-07-09 12:26:10 -0700295 final HasDigest metadata;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100296 @Nullable final RootedPath realPath;
297 @Nullable final PathFragment unresolvedSymlinkTarget;
298
kush4b120e72018-07-11 16:21:27 -0700299 FileInfo(
300 FileType type,
fellyf1e30f32019-07-09 12:26:10 -0700301 HasDigest metadata,
kush4b120e72018-07-11 16:21:27 -0700302 @Nullable RootedPath realPath,
kushb39c6932018-07-12 21:25:23 -0700303 @Nullable PathFragment unresolvedSymlinkTarget) {
Googler8b68efe2023-03-15 14:57:50 -0700304 checkNotNull(metadata.getDigest(), metadata);
305 this.type = checkNotNull(type);
kushb39c6932018-07-12 21:25:23 -0700306 this.metadata = metadata;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100307 this.realPath = realPath;
308 this.unresolvedSymlinkTarget = unresolvedSymlinkTarget;
309 }
310
311 @Override
312 public String toString() {
313 if (type.isSymlink()) {
314 return String.format("(%s: link_value=%s, real_path=%s)", type,
315 unresolvedSymlinkTarget.getPathString(), realPath);
316 } else {
317 return String.format("(%s: real_path=%s)", type, realPath);
318 }
319 }
320 }
321
janakrb77246c2021-03-05 14:41:58 -0800322 @Nullable
janakr491e4412022-01-28 15:35:34 -0800323 private static FileInfo lookUpFileInfo(
324 Environment env, TraversalRequest traversal, SyscallCache syscallCache)
janakrb77246c2021-03-05 14:41:58 -0800325 throws IOException, InterruptedException {
Googlerd78fb572022-07-06 11:44:42 -0700326 if (traversal.isRootGenerated()) {
fellyf1e30f32019-07-09 12:26:10 -0700327 HasDigest fsVal = null;
Googlerd78fb572022-07-06 11:44:42 -0700328 if (traversal.root().getOutputArtifact() != null) {
329 Artifact artifact = traversal.root().getOutputArtifact();
lberki36df7ed2019-06-27 06:32:03 -0700330 SkyKey artifactKey = Artifact.key(artifact);
felly5be4dd62018-02-05 11:11:53 -0800331 SkyValue value = env.getValue(artifactKey);
332 if (env.valuesMissing()) {
janakrb77246c2021-03-05 14:41:58 -0800333 return null;
felly5be4dd62018-02-05 11:11:53 -0800334 }
kush95bf7c82017-08-30 00:27:35 +0200335
fellyf1e30f32019-07-09 12:26:10 -0700336 if (value instanceof FileArtifactValue || value instanceof TreeArtifactValue) {
337 fsVal = (HasDigest) value;
janakr8541f6d2019-06-11 14:40:21 -0700338 } else if (value instanceof ActionExecutionValue) {
Googleraed41602020-06-02 12:22:53 -0700339 fsVal = ((ActionExecutionValue) value).getExistingFileArtifactValue(artifact);
felly5be4dd62018-02-05 11:11:53 -0800340 } else {
kushb39c6932018-07-12 21:25:23 -0700341 return NON_EXISTENT_FILE_INFO;
felly5be4dd62018-02-05 11:11:53 -0800342 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100343 }
Googlerd78fb572022-07-06 11:44:42 -0700344 RootedPath realPath = traversal.root().asRootedPath();
345 if (traversal.strictOutputFiles()) {
Googler8b68efe2023-03-15 14:57:50 -0700346 checkNotNull(fsVal, "Strict Fileset output tree has null FileArtifactValue");
Googlerc8acdbd2018-11-08 10:47:15 -0800347 return new FileInfo(
Googlerd78fb572022-07-06 11:44:42 -0700348 fsVal instanceof TreeArtifactValue ? FileType.DIRECTORY : FileType.FILE,
jcaterb11c6e22020-04-03 11:40:16 -0700349 fsVal,
350 realPath,
351 null);
felly5be4dd62018-02-05 11:11:53 -0800352 } else {
felly6c399d62018-07-18 15:55:59 -0700353 // FileArtifactValue does not currently track symlinks. If it did, we could potentially
354 // remove some of the filesystem operations we're doing here.
Googlerd78fb572022-07-06 11:44:42 -0700355 Path path = traversal.root().asPath();
fellyf1e30f32019-07-09 12:26:10 -0700356 FileStateValue fileState =
Googlerd78fb572022-07-06 11:44:42 -0700357 FileStateValue.create(traversal.root().asRootedPath(), syscallCache, null);
fellyf1e30f32019-07-09 12:26:10 -0700358 if (fileState.getType() == FileStateType.NONEXISTENT) {
359 throw new IOException("Missing file: " + path);
360 }
felly6c399d62018-07-18 15:55:59 -0700361 FileStatus followStat = path.statIfFound(Symlinks.FOLLOW);
362 FileType type;
363 PathFragment unresolvedLinkTarget = null;
364 if (followStat == null) {
365 type = FileType.DANGLING_SYMLINK;
fellyf1e30f32019-07-09 12:26:10 -0700366 if (fileState.getType() != FileStateType.SYMLINK) {
367 throw new IOException("Expected symlink for " + path + ", but got: " + fileState);
felly6c399d62018-07-18 15:55:59 -0700368 }
369 unresolvedLinkTarget = path.readSymbolicLink();
fellyf1e30f32019-07-09 12:26:10 -0700370 } else if (fileState.getType() == FileStateType.REGULAR_FILE) {
felly6c399d62018-07-18 15:55:59 -0700371 type = FileType.FILE;
fellyf1e30f32019-07-09 12:26:10 -0700372 } else if (fileState.getType() == FileStateType.DIRECTORY) {
felly6c399d62018-07-18 15:55:59 -0700373 type = FileType.DIRECTORY;
374 } else {
375 unresolvedLinkTarget = path.readSymbolicLink();
376 realPath =
377 RootedPath.toRootedPath(
378 Root.absoluteRoot(path.getFileSystem()), path.resolveSymbolicLinks());
379 type = followStat.isFile() ? FileType.SYMLINK_TO_FILE : FileType.SYMLINK_TO_DIRECTORY;
380 }
fellyf1e30f32019-07-09 12:26:10 -0700381 if (fsVal == null) {
382 fsVal = fileState;
383 }
janakr491e4412022-01-28 15:35:34 -0800384 return new FileInfo(
385 type, withDigest(fsVal, path, syscallCache), realPath, unresolvedLinkTarget);
felly5be4dd62018-02-05 11:11:53 -0800386 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100387 } else {
felly5be4dd62018-02-05 11:11:53 -0800388 // Stat the file.
389 FileValue fileValue =
Googlerd78fb572022-07-06 11:44:42 -0700390 (FileValue)
391 env.getValueOrThrow(
392 FileValue.key(traversal.root().asRootedPath()), IOException.class);
felly5be4dd62018-02-05 11:11:53 -0800393
394 if (env.valuesMissing()) {
janakrb77246c2021-03-05 14:41:58 -0800395 return null;
felly5be4dd62018-02-05 11:11:53 -0800396 }
Googler9fb98e32023-07-13 07:40:38 -0700397 return toFileInfo(fileValue, env, traversal.root().asPath(), syscallCache);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100398 }
399 }
400
Googlere0ba7962022-08-11 08:37:36 -0700401 @Nullable
402 private static FileInfo toFileInfo(
Googler9fb98e32023-07-13 07:40:38 -0700403 FileValue fileValue, Environment env, Path path, SyscallCache syscallCache)
Googlere0ba7962022-08-11 08:37:36 -0700404 throws IOException, InterruptedException {
405 if (fileValue.unboundedAncestorSymlinkExpansionChain() != null) {
Googler9fb98e32023-07-13 07:40:38 -0700406 SkyKey uniquenessKey =
407 FileSymlinkInfiniteExpansionUniquenessFunction.key(
408 fileValue.unboundedAncestorSymlinkExpansionChain());
409 env.getValue(uniquenessKey);
410 if (env.valuesMissing()) {
411 return null;
Googlere0ba7962022-08-11 08:37:36 -0700412 }
413
414 throw new FileSymlinkInfiniteExpansionException(
415 fileValue.pathToUnboundedAncestorSymlinkExpansionChain(),
416 fileValue.unboundedAncestorSymlinkExpansionChain());
417 }
418
419 if (!fileValue.exists()) {
420 // If it doesn't exist, or it's a dangling symlink, we still want to handle that gracefully.
421 return new FileInfo(
422 fileValue.isSymlink() ? FileType.DANGLING_SYMLINK : FileType.NONEXISTENT,
423 withDigest(fileValue.realFileStateValue(), null, syscallCache),
424 null,
425 fileValue.isSymlink() ? fileValue.getUnresolvedLinkTarget() : null);
426 }
427
428 // If it exists, it may either be a symlink or a file/directory.
429 PathFragment unresolvedLinkTarget = null;
430 FileType type;
431 if (fileValue.isSymlink()) {
432 unresolvedLinkTarget = fileValue.getUnresolvedLinkTarget();
433 type = fileValue.isDirectory() ? FileType.SYMLINK_TO_DIRECTORY : FileType.SYMLINK_TO_FILE;
434 } else {
435 type = fileValue.isDirectory() ? FileType.DIRECTORY : FileType.FILE;
436 }
437 return new FileInfo(
438 type,
439 withDigest(fileValue.realFileStateValue(), path, syscallCache),
440 fileValue.realRootedPath(),
441 unresolvedLinkTarget);
442 }
443
Googler5284e6c2019-10-30 07:43:54 -0700444 /**
445 * Transform the HasDigest to the appropriate type based on the current state of the digest. If
446 * fsVal is type RegularFileStateValue or FileArtifactValue and has a valid digest value, then we
447 * want to convert it to a new FileArtifactValue type. Otherwise if they are of the two
448 * forementioned types but do not have a digest, then we will create a FileArtifactValue using its
449 * {@link Path}. Otherwise we will fingerprint the digest and return it as a new {@link
450 * HasDigest.ByteStringDigest} object.
451 *
452 * @param fsVal - the HasDigest value that was in the graph.
453 * @param path - the Path of the digest.
454 * @return transformed HasDigest value based on the digest field and object type.
455 */
456 @VisibleForTesting
janakre2853222022-03-08 10:52:18 -0800457 static HasDigest withDigest(HasDigest fsVal, Path path, XattrProvider syscallCache)
janakr491e4412022-01-28 15:35:34 -0800458 throws IOException {
Googler6f48f1c2024-04-16 14:29:09 -0700459 if (fsVal instanceof FileStateValue fsv) {
460 if (fsv instanceof RegularFileStateValueWithDigest rfsv) {
Googler8b68efe2023-03-15 14:57:50 -0700461 return FileArtifactValue.createForVirtualActionInput(rfsv.getDigest(), rfsv.getSize());
Googler6f48f1c2024-04-16 14:29:09 -0700462 } else if (fsv instanceof RegularFileStateValueWithContentsProxy rfsv) {
Googler8b68efe2023-03-15 14:57:50 -0700463 return FileArtifactValue.createForNormalFileUsingPath(path, rfsv.getSize(), syscallCache);
Googler5284e6c2019-10-30 07:43:54 -0700464 }
Googler8b68efe2023-03-15 14:57:50 -0700465
janakre430dc02021-04-28 12:28:57 -0700466 return new HasDigest.ByteStringDigest(fsv.getValueFingerprint());
Googler6f48f1c2024-04-16 14:29:09 -0700467 } else if (fsVal instanceof FileArtifactValue fav) {
Googler5284e6c2019-10-30 07:43:54 -0700468 if (fav.getDigest() != null) {
469 return fav;
470 }
471
472 // In the case there is a directory, the HasDigest value should not be converted. Otherwise,
473 // if the HasDigest value is a file, convert it using the Path and size values.
474 return fav.getType().isFile()
janakr491e4412022-01-28 15:35:34 -0800475 ? FileArtifactValue.createForNormalFileUsingPath(path, fav.getSize(), syscallCache)
janakre430dc02021-04-28 12:28:57 -0700476 : new HasDigest.ByteStringDigest(fav.getValueFingerprint());
fellyf1e30f32019-07-09 12:26:10 -0700477 }
478 return fsVal;
479 }
480
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100481 private static final class PkgLookupResult {
482 private enum Type {
483 CONFLICT, DIRECTORY, PKG
484 }
485
Googler8b68efe2023-03-15 14:57:50 -0700486 private final PkgLookupResult.Type type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100487 final TraversalRequest traversal;
488 final FileInfo rootInfo;
489
490 /** Result for a generated directory that conflicts with a source package. */
491 static PkgLookupResult conflict(TraversalRequest traversal, FileInfo rootInfo) {
Googler8b68efe2023-03-15 14:57:50 -0700492 return new PkgLookupResult(PkgLookupResult.Type.CONFLICT, traversal, rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100493 }
494
495 /** Result for a source or generated directory (not a package). */
496 static PkgLookupResult directory(TraversalRequest traversal, FileInfo rootInfo) {
Googler8b68efe2023-03-15 14:57:50 -0700497 return new PkgLookupResult(PkgLookupResult.Type.DIRECTORY, traversal, rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100498 }
499
500 /** Result for a package, i.e. a directory with a BUILD file. */
501 static PkgLookupResult pkg(TraversalRequest traversal, FileInfo rootInfo) {
Googler8b68efe2023-03-15 14:57:50 -0700502 return new PkgLookupResult(PkgLookupResult.Type.PKG, traversal, rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100503 }
504
Googler8b68efe2023-03-15 14:57:50 -0700505 private PkgLookupResult(
506 PkgLookupResult.Type type, TraversalRequest traversal, FileInfo rootInfo) {
507 this.type = checkNotNull(type);
508 this.traversal = checkNotNull(traversal);
509 this.rootInfo = checkNotNull(rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100510 }
511
512 boolean isPackage() {
Googler8b68efe2023-03-15 14:57:50 -0700513 return type == PkgLookupResult.Type.PKG;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100514 }
515
516 boolean isConflicting() {
Googler8b68efe2023-03-15 14:57:50 -0700517 return type == PkgLookupResult.Type.CONFLICT;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100518 }
519
520 @Override
521 public String toString() {
522 return String.format("(%s: info=%s, traversal=%s)", type, rootInfo, traversal);
523 }
524 }
525
526 /**
527 * Checks whether the {@code traversal}'s path refers to a package directory.
528 *
529 * @return the result of the lookup; it contains potentially new {@link TraversalRequest} and
530 * {@link FileInfo} so the caller should use these instead of the old ones (this happens when
531 * a package is found, but under a different root than expected)
532 */
Googler5cc598c2022-07-06 03:29:45 -0700533 @Nullable
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000534 private static PkgLookupResult checkIfPackage(
janakr491e4412022-01-28 15:35:34 -0800535 Environment env, TraversalRequest traversal, FileInfo rootInfo, SyscallCache syscallCache)
janakrb77246c2021-03-05 14:41:58 -0800536 throws IOException, InterruptedException, BuildFileNotFoundException {
Googler8b68efe2023-03-15 14:57:50 -0700537 checkArgument(
538 rootInfo.type.exists() && !rootInfo.type.isFile(), "{%s} {%s}", traversal, rootInfo);
janakrb77246c2021-03-05 14:41:58 -0800539 // PackageLookupFunction/dependencies can only throw IOException, BuildFileNotFoundException,
540 // and RepositoryFetchException, and RepositoryFetchException is not in play here. Note that
541 // run-of-the-mill circular symlinks will *not* throw here, and will trigger later errors during
542 // the recursive traversal.
tomlu8cc5dcf2018-01-19 09:28:06 -0800543 PackageLookupValue pkgLookup =
544 (PackageLookupValue)
janakrb77246c2021-03-05 14:41:58 -0800545 env.getValueOrThrow(
Googlerd78fb572022-07-06 11:44:42 -0700546 PackageLookupValue.key(traversal.root().getRelativePart()),
janakrb77246c2021-03-05 14:41:58 -0800547 BuildFileNotFoundException.class,
548 IOException.class);
549 if (env.valuesMissing()) {
550 return null;
551 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100552
553 if (pkgLookup.packageExists()) {
Googlerd78fb572022-07-06 11:44:42 -0700554 if (traversal.isRootGenerated()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100555 // The traversal's root was a generated directory, but its root-relative path conflicts with
556 // an existing package.
557 return PkgLookupResult.conflict(traversal, rootInfo);
558 } else {
559 // The traversal's root was a source directory and it defines a package.
tomluee6a6862018-01-17 14:36:26 -0800560 Root pkgRoot = pkgLookup.getRoot();
Googlerd78fb572022-07-06 11:44:42 -0700561 if (!pkgRoot.equals(traversal.root().getRootPart())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100562 // However the root of this package is different from what we expected. stat() the real
563 // BUILD file of that package.
564 traversal = traversal.forChangedRootPath(pkgRoot);
janakr491e4412022-01-28 15:35:34 -0800565 rootInfo = lookUpFileInfo(env, traversal, syscallCache);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100566 Verify.verify(rootInfo.type.exists(), "{%s} {%s}", traversal, rootInfo);
567 }
568 return PkgLookupResult.pkg(traversal, rootInfo);
569 }
570 } else {
571 // The traversal's root was a directory (source or generated one), no package exists under the
572 // same root-relative path.
573 return PkgLookupResult.directory(traversal, rootInfo);
574 }
575 }
576
577 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100578 * Creates results for a file or for a symlink that points to one.
579 *
580 * <p>A symlink may be direct (points to a file) or transitive (points at a direct or transitive
581 * symlink).
582 */
Googler0d4739e2023-10-30 10:00:55 -0700583 private static RecursiveFilesystemTraversalValue resultForFileRoot(RootedPath path, FileInfo info)
584 throws InconsistentFilesystemException {
585 if (!info.type.isFile() || !info.type.exists()) {
586 throw new InconsistentFilesystemException(
587 String.format(
588 "We were previously told %s was an existing file but it's actually %s", path, info));
589 }
590
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100591 if (info.type.isSymlink()) {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000592 return RecursiveFilesystemTraversalValue.of(
593 ResolvedFileFactory.symlinkToFile(
kushb39c6932018-07-12 21:25:23 -0700594 info.realPath, path, info.unresolvedSymlinkTarget, info.metadata));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100595 } else {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000596 return RecursiveFilesystemTraversalValue.of(
kushb39c6932018-07-12 21:25:23 -0700597 ResolvedFileFactory.regularFile(path, info.metadata));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100598 }
599 }
600
emilyguoc07e66d2022-03-23 18:57:24 -0700601 private static RecursiveFilesystemTraversalValue resultForDirectory(
602 TraversalRequest traversal,
603 FileInfo rootInfo,
emilyguo305fcc32022-03-29 10:10:07 -0700604 ImmutableList<RecursiveFilesystemTraversalValue> subdirTraversals) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100605 // Collect transitive closure of files in subdirectories.
606 NestedSetBuilder<ResolvedFile> paths = NestedSetBuilder.stableOrder();
607 for (RecursiveFilesystemTraversalValue child : subdirTraversals) {
608 paths.addTransitive(child.getTransitiveFiles());
609 }
610 ResolvedFile root;
611 if (rootInfo.type.isSymlink()) {
Laszlo Csomor207140f2015-12-07 15:07:33 +0000612 NestedSet<ResolvedFile> children = paths.build();
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000613 root =
614 ResolvedFileFactory.symlinkToDirectory(
615 rootInfo.realPath,
Googlerd78fb572022-07-06 11:44:42 -0700616 traversal.root().asRootedPath(),
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000617 rootInfo.unresolvedSymlinkTarget,
kushb39c6932018-07-12 21:25:23 -0700618 hashDirectorySymlink(children, rootInfo.metadata));
Laszlo Csomor207140f2015-12-07 15:07:33 +0000619 paths = NestedSetBuilder.<ResolvedFile>stableOrder().addTransitive(children).add(root);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100620 } else {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000621 root = ResolvedFileFactory.directory(rootInfo.realPath);
Fabian Meumertzheimfbfab662022-08-19 16:08:17 -0700622 if (traversal.emitEmptyDirectoryNodes() && paths.isEmpty()) {
623 paths.add(root);
624 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100625 }
626 return RecursiveFilesystemTraversalValue.of(root, paths.build());
627 }
628
fellyf1e30f32019-07-09 12:26:10 -0700629 private static HasDigest hashDirectorySymlink(
ulfjack61216fd2019-12-18 12:31:03 -0800630 NestedSet<ResolvedFile> children, HasDigest metadata) {
Laszlo Csomor207140f2015-12-07 15:07:33 +0000631 // If the root is a directory symlink, the associated FileStateValue does not change when the
632 // linked directory's contents change, so we can't use the FileStateValue as metadata like we
633 // do with other ResolvedFile kinds. Instead we compute a metadata hash from the child
634 // elements and return that as the ResolvedFile's metadata hash.
fellyf1e30f32019-07-09 12:26:10 -0700635 Fingerprint fp = new Fingerprint();
636 fp.addBytes(metadata.getDigest());
ulfjack61216fd2019-12-18 12:31:03 -0800637 for (ResolvedFile file : children.toList()) {
fellyf1e30f32019-07-09 12:26:10 -0700638 fp.addPath(file.getNameInSymlinkTree());
639 fp.addBytes(file.getMetadata().getDigest());
Laszlo Csomor207140f2015-12-07 15:07:33 +0000640 }
fellyf1e30f32019-07-09 12:26:10 -0700641 byte[] result = fp.digestAndReset();
642 return new HasDigest.ByteStringDigest(result);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000643 }
644
emilyguoc07e66d2022-03-23 18:57:24 -0700645 /** Requests Skyframe to compute the dependent values and returns them. */
janakrb77246c2021-03-05 14:41:58 -0800646 @Nullable
emilyguo305fcc32022-03-29 10:10:07 -0700647 private ImmutableList<RecursiveFilesystemTraversalValue> traverseChildren(
emilyguoc07e66d2022-03-23 18:57:24 -0700648 Environment env, TraversalRequest traversal)
649 throws InterruptedException, RecursiveFilesystemTraversalFunctionException, IOException {
Googlere0ba7962022-08-11 08:37:36 -0700650 return traversal.isRootGenerated()
651 ? traverseGeneratedChildren(env, traversal)
652 : traverseSourceChildren(env, traversal);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100653 }
654
Googlere0ba7962022-08-11 08:37:36 -0700655 @Nullable
656 private ImmutableList<RecursiveFilesystemTraversalValue> traverseGeneratedChildren(
657 Environment env, TraversalRequest traversal)
658 throws InterruptedException, RecursiveFilesystemTraversalFunctionException, IOException {
659 // If we're dealing with an output file, read the directory directly instead of creating
660 // filesystem nodes under the output tree.
661 Collection<Dirent> dirents = traversal.root().asPath().readdir(Symlinks.FOLLOW);
662 if (dirents.isEmpty()) {
663 return ImmutableList.of();
664 }
665 List<Dirent> sortedDirents = new ArrayList<>(dirents);
666 Collections.sort(sortedDirents);
667
668 ImmutableList.Builder<RecursiveFilesystemTraversalValue> childValues =
669 ImmutableList.builderWithExpectedSize(dirents.size());
670 for (Dirent dirent : sortedDirents) {
671 RecursiveFilesystemTraversalValue childValue =
672 compute(traversal.forChildEntry(dirent.getName()), env);
673 if (childValue != null) {
674 childValues.add(childValue);
675 }
676 }
677 return env.valuesMissing() ? null : childValues.build();
678 }
679
680 @Nullable
681 private ImmutableList<RecursiveFilesystemTraversalValue> traverseSourceChildren(
682 Environment env, TraversalRequest traversal) throws InterruptedException, IOException {
683 DirectoryListingValue dirListingValue =
684 (DirectoryListingValue)
685 env.getValueOrThrow(
686 DirectoryListingValue.key(traversal.root().asRootedPath()), IOException.class);
687 if (dirListingValue == null) {
688 return null;
689 }
690 Dirents dirents = dirListingValue.getDirents();
691 if (dirents.size() == 0) {
692 return ImmutableList.of();
693 }
694
695 List<SkyKey> childKeys = new ArrayList<>(dirents.size());
696 for (Dirent dirent : dirents) {
697 TraversalRequest childRequest = traversal.forChildEntry(dirent.getName());
698 // For source files, request the FileValue directly instead of another TraversalRequest. This
699 // makes the base case of the recursive traversal a directory with no subdirectories instead
700 // of a file, greatly reducing the number of skyframe nodes for directories with many files.
701 if (dirent.getType() == Dirent.Type.FILE) {
702 childKeys.add(FileValue.key(childRequest.root().asRootedPath()));
703 } else {
704 childKeys.add(childRequest);
705 }
706 }
707
Googlerb5052b82022-11-03 11:33:56 -0700708 SkyframeLookupResult result = env.getValuesAndExceptions(childKeys);
Googlere0ba7962022-08-11 08:37:36 -0700709 ImmutableList.Builder<RecursiveFilesystemTraversalValue> childValues =
710 ImmutableList.builderWithExpectedSize(childKeys.size());
711 for (SkyKey key : childKeys) {
Googlerb5052b82022-11-03 11:33:56 -0700712 SkyValue value = result.get(key);
Googlere0ba7962022-08-11 08:37:36 -0700713 if (value == null) {
714 continue;
715 }
Googler6f48f1c2024-04-16 14:29:09 -0700716 if (key instanceof FileValue.Key fileKey) {
Googlere0ba7962022-08-11 08:37:36 -0700717 FileInfo fileInfo =
Googler9fb98e32023-07-13 07:40:38 -0700718 toFileInfo((FileValue) value, env, fileKey.argument().asPath(), syscallCache);
Googlere0ba7962022-08-11 08:37:36 -0700719 if (fileInfo != null) {
720 childValues.add(resultForFileRoot(fileKey.argument(), fileInfo));
721 }
722 } else {
723 childValues.add((RecursiveFilesystemTraversalValue) value);
724 }
725 }
726 return env.valuesMissing() ? null : childValues.build();
727 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100728}