blob: c5f15b2887b70563a5f960f03dd23aa4fc8c3a29 [file] [log] [blame]
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +00001// Copyright 2016 The Bazel Authors. All rights reserved.
2//
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
Googler9bd88332019-03-19 18:09:44 -070016import static com.google.common.collect.ImmutableSet.toImmutableSet;
17
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000018import com.google.common.base.MoreObjects;
19import com.google.common.collect.ImmutableMap;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000020import com.google.common.collect.ImmutableSet;
Googler9bd88332019-03-19 18:09:44 -070021import com.google.common.collect.ImmutableSortedMap;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000022import com.google.common.collect.Maps;
Rumou Duana77f32c2016-04-13 21:59:21 +000023import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
shahan602cc852018-06-06 20:09:57 -070024import com.google.devtools.build.lib.actions.FileArtifactValue;
Shreya Bhattarai141b6c22016-08-22 22:00:24 +000025import com.google.devtools.build.lib.actions.cache.DigestUtils;
janakrb4e8cd72018-02-27 15:39:08 -080026import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
mjhalupka61020a72018-12-20 10:57:43 -080027import com.google.devtools.build.lib.util.BigIntegerFingerprint;
Googlerfeb26702018-11-06 10:23:45 -080028import com.google.devtools.build.lib.vfs.Dirent;
29import com.google.devtools.build.lib.vfs.Dirent.Type;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000030import com.google.devtools.build.lib.vfs.Path;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000031import com.google.devtools.build.lib.vfs.PathFragment;
Googlerfeb26702018-11-06 10:23:45 -080032import com.google.devtools.build.lib.vfs.Symlinks;
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000033import com.google.devtools.build.skyframe.SkyValue;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000034import java.io.IOException;
mjhalupka61020a72018-12-20 10:57:43 -080035import java.math.BigInteger;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000036import java.util.Arrays;
Googlerfeb26702018-11-06 10:23:45 -080037import java.util.Collection;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000038import java.util.Map;
39import java.util.Set;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000040import javax.annotation.Nullable;
41
42/**
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000043 * Value for TreeArtifacts, which contains a digest and the {@link FileArtifactValue}s of its child
44 * {@link TreeFileArtifact}s.
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000045 */
janakrb4e8cd72018-02-27 15:39:08 -080046@AutoCodec
lpino578ffcc2018-12-06 07:08:06 -080047public class TreeArtifactValue implements SkyValue {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000048
Rumou Duana77f32c2016-04-13 21:59:21 +000049 private final byte[] digest;
Googler9bd88332019-03-19 18:09:44 -070050 private final ImmutableSortedMap<TreeFileArtifact, FileArtifactValue> childData;
mjhalupka61020a72018-12-20 10:57:43 -080051 private BigInteger valueFingerprint;
Rumou Duana77f32c2016-04-13 21:59:21 +000052
janakrb4e8cd72018-02-27 15:39:08 -080053 @AutoCodec.VisibleForSerialization
Googler9bd88332019-03-19 18:09:44 -070054 TreeArtifactValue(
55 byte[] digest, ImmutableSortedMap<TreeFileArtifact, FileArtifactValue> childData) {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000056 this.digest = digest;
Googler9bd88332019-03-19 18:09:44 -070057 this.childData = childData;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000058 }
59
60 /**
buchgr75bb7a92019-03-14 07:09:33 -070061 * Returns a TreeArtifactValue out of the given Artifact-relative path fragments and their
62 * corresponding FileArtifactValues.
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000063 */
Rumou Duanbaeed332016-10-18 15:30:25 +000064 static TreeArtifactValue create(Map<TreeFileArtifact, FileArtifactValue> childFileValues) {
shahan602cc852018-06-06 20:09:57 -070065 Map<String, FileArtifactValue> digestBuilder =
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000066 Maps.newHashMapWithExpectedSize(childFileValues.size());
Rumou Duana77f32c2016-04-13 21:59:21 +000067 for (Map.Entry<TreeFileArtifact, FileArtifactValue> e : childFileValues.entrySet()) {
plf445260d2019-03-15 12:11:03 -070068 digestBuilder.put(e.getKey().getParentRelativePath().getPathString(), e.getValue());
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000069 }
plf445260d2019-03-15 12:11:03 -070070
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000071 return new TreeArtifactValue(
Shreya Bhattarai141b6c22016-08-22 22:00:24 +000072 DigestUtils.fromMetadata(digestBuilder).getDigestBytesUnsafe(),
Googler9bd88332019-03-19 18:09:44 -070073 ImmutableSortedMap.copyOf(childFileValues));
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000074 }
75
Rumou Duanbaeed332016-10-18 15:30:25 +000076 FileArtifactValue getSelfData() {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000077 return FileArtifactValue.createProxy(digest);
78 }
79
shahan602cc852018-06-06 20:09:57 -070080 FileArtifactValue getMetadata() {
ulfjack01776ee2017-06-26 10:33:05 +020081 return getSelfData();
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000082 }
83
Googler9bd88332019-03-19 18:09:44 -070084 ImmutableSet<PathFragment> getChildPaths() {
85 return childData.keySet().stream()
86 .map(TreeFileArtifact::getParentRelativePath)
87 .collect(toImmutableSet());
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000088 }
89
90 @Nullable
Rumou Duanbaeed332016-10-18 15:30:25 +000091 byte[] getDigest() {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000092 return digest.clone();
93 }
94
Rumou Duanbaeed332016-10-18 15:30:25 +000095 Iterable<TreeFileArtifact> getChildren() {
Rumou Duana77f32c2016-04-13 21:59:21 +000096 return childData.keySet();
97 }
98
Googler9bd88332019-03-19 18:09:44 -070099 ImmutableMap<TreeFileArtifact, FileArtifactValue> getChildValues() {
Rumou Duanbaeed332016-10-18 15:30:25 +0000100 return childData;
Rumou Duana77f32c2016-04-13 21:59:21 +0000101 }
102
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000103 @Override
mjhalupka61020a72018-12-20 10:57:43 -0800104 public BigInteger getValueFingerprint() {
105 if (valueFingerprint == null) {
106 BigIntegerFingerprint fp = new BigIntegerFingerprint();
janakr458f07a2019-02-05 13:51:54 -0800107 fp.addDigestedBytes(digest);
mjhalupka61020a72018-12-20 10:57:43 -0800108 valueFingerprint = fp.getFingerprint();
109 }
110 return valueFingerprint;
111 }
112
113 @Override
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000114 public int hashCode() {
115 return Arrays.hashCode(digest);
116 }
117
118 @Override
119 public boolean equals(Object other) {
120 if (this == other) {
121 return true;
122 }
123
124 if (!(other instanceof TreeArtifactValue)) {
125 return false;
126 }
127
128 TreeArtifactValue that = (TreeArtifactValue) other;
Shreya Bhattarai3b096802016-08-17 23:14:52 +0000129 if (!Arrays.equals(digest, that.digest)) {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000130 return false;
131 }
132
133 return childData.equals(that.childData);
134 }
135
136 @Override
137 public String toString() {
138 return MoreObjects.toStringHelper(TreeArtifactValue.class)
139 .add("digest", digest)
140 .add("childData", childData)
141 .toString();
142 }
143
144 /**
shahan602cc852018-06-06 20:09:57 -0700145 * A TreeArtifactValue that represents a missing TreeArtifact. This is occasionally useful because
146 * Java's concurrent collections disallow null members.
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000147 */
shahan602cc852018-06-06 20:09:57 -0700148 static final TreeArtifactValue MISSING_TREE_ARTIFACT =
Googler9bd88332019-03-19 18:09:44 -0700149 new TreeArtifactValue(null, ImmutableSortedMap.of()) {
shahan602cc852018-06-06 20:09:57 -0700150 @Override
151 FileArtifactValue getSelfData() {
152 throw new UnsupportedOperationException();
153 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000154
shahan602cc852018-06-06 20:09:57 -0700155 @Override
156 Iterable<TreeFileArtifact> getChildren() {
157 throw new UnsupportedOperationException();
158 }
Rumou Duana77f32c2016-04-13 21:59:21 +0000159
shahan602cc852018-06-06 20:09:57 -0700160 @Override
Googler9bd88332019-03-19 18:09:44 -0700161 ImmutableMap<TreeFileArtifact, FileArtifactValue> getChildValues() {
shahan602cc852018-06-06 20:09:57 -0700162 throw new UnsupportedOperationException();
163 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000164
shahan602cc852018-06-06 20:09:57 -0700165 @Override
166 FileArtifactValue getMetadata() {
167 throw new UnsupportedOperationException();
168 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000169
shahan602cc852018-06-06 20:09:57 -0700170 @Override
Googler9bd88332019-03-19 18:09:44 -0700171 ImmutableSet<PathFragment> getChildPaths() {
shahan602cc852018-06-06 20:09:57 -0700172 throw new UnsupportedOperationException();
173 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000174
shahan602cc852018-06-06 20:09:57 -0700175 @Nullable
176 @Override
177 byte[] getDigest() {
178 throw new UnsupportedOperationException();
179 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000180
shahan602cc852018-06-06 20:09:57 -0700181 @Override
182 public int hashCode() {
183 return 24; // my favorite number
184 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000185
shahan602cc852018-06-06 20:09:57 -0700186 @Override
187 public boolean equals(Object other) {
188 return this == other;
189 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000190
shahan602cc852018-06-06 20:09:57 -0700191 @Override
192 public String toString() {
193 return "MISSING_TREE_ARTIFACT";
194 }
195 };
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000196
felly09efb3f2018-07-26 07:46:15 -0700197 private static void explodeDirectory(Path treeArtifactPath,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000198 PathFragment pathToExplode, ImmutableSet.Builder<PathFragment> valuesBuilder)
Rumou Duan9ad28cd2016-10-19 19:28:06 +0000199 throws IOException {
Googlerfeb26702018-11-06 10:23:45 -0800200 Path dir = treeArtifactPath.getRelative(pathToExplode);
201 Collection<Dirent> dirents = dir.readdir(Symlinks.NOFOLLOW);
202 for (Dirent dirent : dirents) {
203 PathFragment canonicalSubpathFragment = pathToExplode.getChild(dirent.getName());
204 if (dirent.getType() == Type.DIRECTORY) {
205 explodeDirectory(treeArtifactPath, canonicalSubpathFragment, valuesBuilder);
206 } else if (dirent.getType() == Type.SYMLINK) {
207 Path subpath = dir.getRelative(dirent.getName());
Rumou Duan11730a02016-10-24 20:27:58 +0000208 PathFragment linkTarget = subpath.readSymbolicLinkUnchecked();
Janak Ramakrishnan6b4a8b32017-02-22 19:51:28 +0000209 valuesBuilder.add(canonicalSubpathFragment);
Rumou Duan11730a02016-10-24 20:27:58 +0000210 if (linkTarget.isAbsolute()) {
Janak Ramakrishnan6b4a8b32017-02-22 19:51:28 +0000211 // We tolerate absolute symlinks here. They will probably be dangling if any downstream
212 // consumer tries to read them, but let that be downstream's problem.
213 continue;
Rumou Duan11730a02016-10-24 20:27:58 +0000214 }
Rumou Duan11730a02016-10-24 20:27:58 +0000215 // We visit each path segment of the link target to catch any path traversal outside of the
216 // TreeArtifact root directory. For example, for TreeArtifact a/b/c, it is possible to have
217 // a symlink, a/b/c/sym_link that points to ../outside_dir/../c/link_target. Although this
218 // symlink points to a file under the TreeArtifact, the link target traverses outside of the
219 // TreeArtifact into a/b/outside_dir.
220 PathFragment intermediatePath = canonicalSubpathFragment.getParentDirectory();
221 for (String pathSegment : linkTarget.getSegments()) {
tomlua729b9b2018-02-08 15:32:00 -0800222 intermediatePath = intermediatePath.getRelative(pathSegment);
Rumou Duan11730a02016-10-24 20:27:58 +0000223 if (intermediatePath.containsUplevelReferences()) {
224 String errorMessage = String.format(
225 "A TreeArtifact may not contain relative symlinks whose target paths traverse "
226 + "outside of the TreeArtifact, found %s pointing to %s.",
227 subpath,
228 linkTarget);
229 throw new IOException(errorMessage);
230 }
231 }
Googlerfeb26702018-11-06 10:23:45 -0800232 } else if (dirent.getType() == Type.FILE) {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000233 valuesBuilder.add(canonicalSubpathFragment);
234 } else {
235 // We shouldn't ever reach here.
Googlerfeb26702018-11-06 10:23:45 -0800236 Path subpath = dir.getRelative(dirent.getName());
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000237 throw new IllegalStateException("Could not determine type of file " + subpath);
238 }
239 }
240 }
241
242 /**
lpino578ffcc2018-12-06 07:08:06 -0800243 * Recursively get all child files in a directory (excluding child directories themselves, but
244 * including all files in them).
245 *
246 * @throws IOException if there is any problem reading or validating outputs under the given tree
247 * artifact.
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000248 */
lpino578ffcc2018-12-06 07:08:06 -0800249 public static Set<PathFragment> explodeDirectory(Path treeArtifactPath) throws IOException {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000250 ImmutableSet.Builder<PathFragment> explodedDirectory = ImmutableSet.builder();
felly09efb3f2018-07-26 07:46:15 -0700251 explodeDirectory(treeArtifactPath, PathFragment.EMPTY_FRAGMENT, explodedDirectory);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000252 return explodedDirectory.build();
253 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000254}