blob: dd72ee2aabfb5a4cda6e8f2428c44edff43d6931 [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
mjhalupka287c1e72018-08-10 11:57:28 -070016import com.google.common.base.Function;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000017import com.google.common.base.MoreObjects;
18import com.google.common.collect.ImmutableMap;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000019import com.google.common.collect.ImmutableSet;
Rumou Duana77f32c2016-04-13 21:59:21 +000020import com.google.common.collect.Iterables;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000021import com.google.common.collect.Maps;
mjhalupka287c1e72018-08-10 11:57:28 -070022import com.google.devtools.build.lib.actions.Artifact;
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 {
mjhalupka287c1e72018-08-10 11:57:28 -070048 private static final Function<Artifact, PathFragment> PARENT_RELATIVE_PATHS =
49 new Function<Artifact, PathFragment>() {
50 @Override
51 public PathFragment apply(Artifact artifact) {
52 return artifact.getParentRelativePath();
53 }
54 };
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000055
Rumou Duana77f32c2016-04-13 21:59:21 +000056 private final byte[] digest;
mjhalupka287c1e72018-08-10 11:57:28 -070057 private final Map<TreeFileArtifact, FileArtifactValue> childData;
mjhalupka61020a72018-12-20 10:57:43 -080058 private BigInteger valueFingerprint;
Rumou Duana77f32c2016-04-13 21:59:21 +000059
janakrb4e8cd72018-02-27 15:39:08 -080060 @AutoCodec.VisibleForSerialization
buchgrdcf96002019-03-13 11:32:30 -070061 TreeArtifactValue(byte[] digest, Map<TreeFileArtifact, FileArtifactValue> childData) {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000062 this.digest = digest;
mjhalupka287c1e72018-08-10 11:57:28 -070063 this.childData = ImmutableMap.copyOf(childData);
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000064 }
65
66 /**
buchgrdcf96002019-03-13 11:32:30 -070067 * Returns a TreeArtifactValue out of the given Artifact-relative path fragments
68 * and their corresponding FileArtifactValues.
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000069 */
Rumou Duanbaeed332016-10-18 15:30:25 +000070 static TreeArtifactValue create(Map<TreeFileArtifact, FileArtifactValue> childFileValues) {
shahan602cc852018-06-06 20:09:57 -070071 Map<String, FileArtifactValue> digestBuilder =
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000072 Maps.newHashMapWithExpectedSize(childFileValues.size());
Rumou Duana77f32c2016-04-13 21:59:21 +000073 for (Map.Entry<TreeFileArtifact, FileArtifactValue> e : childFileValues.entrySet()) {
buchgrdcf96002019-03-13 11:32:30 -070074 digestBuilder.put(e.getKey().getParentRelativePath().getPathString(), e.getValue());
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000075 }
buchgrdcf96002019-03-13 11:32:30 -070076
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000077 return new TreeArtifactValue(
Shreya Bhattarai141b6c22016-08-22 22:00:24 +000078 DigestUtils.fromMetadata(digestBuilder).getDigestBytesUnsafe(),
buchgrdcf96002019-03-13 11:32:30 -070079 ImmutableMap.copyOf(childFileValues));
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000080 }
81
Rumou Duanbaeed332016-10-18 15:30:25 +000082 FileArtifactValue getSelfData() {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000083 return FileArtifactValue.createProxy(digest);
84 }
85
shahan602cc852018-06-06 20:09:57 -070086 FileArtifactValue getMetadata() {
ulfjack01776ee2017-06-26 10:33:05 +020087 return getSelfData();
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000088 }
89
Rumou Duanbaeed332016-10-18 15:30:25 +000090 Set<PathFragment> getChildPaths() {
mjhalupka287c1e72018-08-10 11:57:28 -070091 return ImmutableSet.copyOf(Iterables.transform(childData.keySet(), PARENT_RELATIVE_PATHS));
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000092 }
93
94 @Nullable
Rumou Duanbaeed332016-10-18 15:30:25 +000095 byte[] getDigest() {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +000096 return digest.clone();
97 }
98
Rumou Duanbaeed332016-10-18 15:30:25 +000099 Iterable<TreeFileArtifact> getChildren() {
Rumou Duana77f32c2016-04-13 21:59:21 +0000100 return childData.keySet();
101 }
102
Rumou Duanbaeed332016-10-18 15:30:25 +0000103 Map<TreeFileArtifact, FileArtifactValue> getChildValues() {
104 return childData;
Rumou Duana77f32c2016-04-13 21:59:21 +0000105 }
106
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000107 @Override
mjhalupka61020a72018-12-20 10:57:43 -0800108 public BigInteger getValueFingerprint() {
109 if (valueFingerprint == null) {
110 BigIntegerFingerprint fp = new BigIntegerFingerprint();
janakr458f07a2019-02-05 13:51:54 -0800111 fp.addDigestedBytes(digest);
mjhalupka61020a72018-12-20 10:57:43 -0800112 valueFingerprint = fp.getFingerprint();
113 }
114 return valueFingerprint;
115 }
116
117 @Override
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000118 public int hashCode() {
119 return Arrays.hashCode(digest);
120 }
121
122 @Override
123 public boolean equals(Object other) {
124 if (this == other) {
125 return true;
126 }
127
128 if (!(other instanceof TreeArtifactValue)) {
129 return false;
130 }
131
132 TreeArtifactValue that = (TreeArtifactValue) other;
Shreya Bhattarai3b096802016-08-17 23:14:52 +0000133 if (!Arrays.equals(digest, that.digest)) {
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000134 return false;
135 }
136
137 return childData.equals(that.childData);
138 }
139
140 @Override
141 public String toString() {
142 return MoreObjects.toStringHelper(TreeArtifactValue.class)
143 .add("digest", digest)
144 .add("childData", childData)
145 .toString();
146 }
147
148 /**
shahan602cc852018-06-06 20:09:57 -0700149 * A TreeArtifactValue that represents a missing TreeArtifact. This is occasionally useful because
150 * Java's concurrent collections disallow null members.
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000151 */
shahan602cc852018-06-06 20:09:57 -0700152 static final TreeArtifactValue MISSING_TREE_ARTIFACT =
buchgrdcf96002019-03-13 11:32:30 -0700153 new TreeArtifactValue(null, ImmutableMap.<TreeFileArtifact, FileArtifactValue>of()) {
shahan602cc852018-06-06 20:09:57 -0700154 @Override
155 FileArtifactValue getSelfData() {
156 throw new UnsupportedOperationException();
157 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000158
shahan602cc852018-06-06 20:09:57 -0700159 @Override
160 Iterable<TreeFileArtifact> getChildren() {
161 throw new UnsupportedOperationException();
162 }
Rumou Duana77f32c2016-04-13 21:59:21 +0000163
shahan602cc852018-06-06 20:09:57 -0700164 @Override
165 Map<TreeFileArtifact, FileArtifactValue> getChildValues() {
166 throw new UnsupportedOperationException();
167 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000168
shahan602cc852018-06-06 20:09:57 -0700169 @Override
170 FileArtifactValue getMetadata() {
171 throw new UnsupportedOperationException();
172 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000173
shahan602cc852018-06-06 20:09:57 -0700174 @Override
175 Set<PathFragment> getChildPaths() {
176 throw new UnsupportedOperationException();
177 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000178
shahan602cc852018-06-06 20:09:57 -0700179 @Nullable
180 @Override
181 byte[] getDigest() {
182 throw new UnsupportedOperationException();
183 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000184
shahan602cc852018-06-06 20:09:57 -0700185 @Override
186 public int hashCode() {
187 return 24; // my favorite number
188 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000189
shahan602cc852018-06-06 20:09:57 -0700190 @Override
191 public boolean equals(Object other) {
192 return this == other;
193 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000194
shahan602cc852018-06-06 20:09:57 -0700195 @Override
196 public String toString() {
197 return "MISSING_TREE_ARTIFACT";
198 }
199 };
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000200
felly09efb3f2018-07-26 07:46:15 -0700201 private static void explodeDirectory(Path treeArtifactPath,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000202 PathFragment pathToExplode, ImmutableSet.Builder<PathFragment> valuesBuilder)
Rumou Duan9ad28cd2016-10-19 19:28:06 +0000203 throws IOException {
Googlerfeb26702018-11-06 10:23:45 -0800204 Path dir = treeArtifactPath.getRelative(pathToExplode);
205 Collection<Dirent> dirents = dir.readdir(Symlinks.NOFOLLOW);
206 for (Dirent dirent : dirents) {
207 PathFragment canonicalSubpathFragment = pathToExplode.getChild(dirent.getName());
208 if (dirent.getType() == Type.DIRECTORY) {
209 explodeDirectory(treeArtifactPath, canonicalSubpathFragment, valuesBuilder);
210 } else if (dirent.getType() == Type.SYMLINK) {
211 Path subpath = dir.getRelative(dirent.getName());
Rumou Duan11730a02016-10-24 20:27:58 +0000212 PathFragment linkTarget = subpath.readSymbolicLinkUnchecked();
Janak Ramakrishnan6b4a8b32017-02-22 19:51:28 +0000213 valuesBuilder.add(canonicalSubpathFragment);
Rumou Duan11730a02016-10-24 20:27:58 +0000214 if (linkTarget.isAbsolute()) {
Janak Ramakrishnan6b4a8b32017-02-22 19:51:28 +0000215 // We tolerate absolute symlinks here. They will probably be dangling if any downstream
216 // consumer tries to read them, but let that be downstream's problem.
217 continue;
Rumou Duan11730a02016-10-24 20:27:58 +0000218 }
Rumou Duan11730a02016-10-24 20:27:58 +0000219 // We visit each path segment of the link target to catch any path traversal outside of the
220 // TreeArtifact root directory. For example, for TreeArtifact a/b/c, it is possible to have
221 // a symlink, a/b/c/sym_link that points to ../outside_dir/../c/link_target. Although this
222 // symlink points to a file under the TreeArtifact, the link target traverses outside of the
223 // TreeArtifact into a/b/outside_dir.
224 PathFragment intermediatePath = canonicalSubpathFragment.getParentDirectory();
225 for (String pathSegment : linkTarget.getSegments()) {
tomlua729b9b2018-02-08 15:32:00 -0800226 intermediatePath = intermediatePath.getRelative(pathSegment);
Rumou Duan11730a02016-10-24 20:27:58 +0000227 if (intermediatePath.containsUplevelReferences()) {
228 String errorMessage = String.format(
229 "A TreeArtifact may not contain relative symlinks whose target paths traverse "
230 + "outside of the TreeArtifact, found %s pointing to %s.",
231 subpath,
232 linkTarget);
233 throw new IOException(errorMessage);
234 }
235 }
Googlerfeb26702018-11-06 10:23:45 -0800236 } else if (dirent.getType() == Type.FILE) {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000237 valuesBuilder.add(canonicalSubpathFragment);
238 } else {
239 // We shouldn't ever reach here.
Googlerfeb26702018-11-06 10:23:45 -0800240 Path subpath = dir.getRelative(dirent.getName());
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000241 throw new IllegalStateException("Could not determine type of file " + subpath);
242 }
243 }
244 }
245
246 /**
lpino578ffcc2018-12-06 07:08:06 -0800247 * Recursively get all child files in a directory (excluding child directories themselves, but
248 * including all files in them).
249 *
250 * @throws IOException if there is any problem reading or validating outputs under the given tree
251 * artifact.
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000252 */
lpino578ffcc2018-12-06 07:08:06 -0800253 public static Set<PathFragment> explodeDirectory(Path treeArtifactPath) throws IOException {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000254 ImmutableSet.Builder<PathFragment> explodedDirectory = ImmutableSet.builder();
felly09efb3f2018-07-26 07:46:15 -0700255 explodeDirectory(treeArtifactPath, PathFragment.EMPTY_FRAGMENT, explodedDirectory);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000256 return explodedDirectory.build();
257 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000258}