Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 1 | // 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. |
| 14 | package com.google.devtools.build.lib.skyframe; |
| 15 | |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 16 | import static com.google.common.collect.ImmutableSet.toImmutableSet; |
| 17 | |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 18 | import com.google.common.base.MoreObjects; |
| 19 | import com.google.common.collect.ImmutableMap; |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 20 | import com.google.common.collect.ImmutableSet; |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 21 | import com.google.common.collect.ImmutableSortedMap; |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 22 | import com.google.common.collect.Maps; |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 23 | import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 24 | import com.google.devtools.build.lib.actions.FileArtifactValue; |
Shreya Bhattarai | 141b6c2 | 2016-08-22 22:00:24 +0000 | [diff] [blame] | 25 | import com.google.devtools.build.lib.actions.cache.DigestUtils; |
janakr | b4e8cd7 | 2018-02-27 15:39:08 -0800 | [diff] [blame] | 26 | import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; |
mjhalupka | 61020a7 | 2018-12-20 10:57:43 -0800 | [diff] [blame] | 27 | import com.google.devtools.build.lib.util.BigIntegerFingerprint; |
Googler | feb2670 | 2018-11-06 10:23:45 -0800 | [diff] [blame] | 28 | import com.google.devtools.build.lib.vfs.Dirent; |
| 29 | import com.google.devtools.build.lib.vfs.Dirent.Type; |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 30 | import com.google.devtools.build.lib.vfs.Path; |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 31 | import com.google.devtools.build.lib.vfs.PathFragment; |
Googler | feb2670 | 2018-11-06 10:23:45 -0800 | [diff] [blame] | 32 | import com.google.devtools.build.lib.vfs.Symlinks; |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 33 | import com.google.devtools.build.skyframe.SkyValue; |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 34 | import java.io.IOException; |
mjhalupka | 61020a7 | 2018-12-20 10:57:43 -0800 | [diff] [blame] | 35 | import java.math.BigInteger; |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 36 | import java.util.Arrays; |
Googler | feb2670 | 2018-11-06 10:23:45 -0800 | [diff] [blame] | 37 | import java.util.Collection; |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 38 | import java.util.Map; |
| 39 | import java.util.Set; |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 40 | import javax.annotation.Nullable; |
| 41 | |
| 42 | /** |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 43 | * Value for TreeArtifacts, which contains a digest and the {@link FileArtifactValue}s of its child |
| 44 | * {@link TreeFileArtifact}s. |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 45 | */ |
janakr | b4e8cd7 | 2018-02-27 15:39:08 -0800 | [diff] [blame] | 46 | @AutoCodec |
lpino | 578ffcc | 2018-12-06 07:08:06 -0800 | [diff] [blame] | 47 | public class TreeArtifactValue implements SkyValue { |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 48 | |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 49 | private final byte[] digest; |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 50 | private final ImmutableSortedMap<TreeFileArtifact, FileArtifactValue> childData; |
mjhalupka | 61020a7 | 2018-12-20 10:57:43 -0800 | [diff] [blame] | 51 | private BigInteger valueFingerprint; |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 52 | |
janakr | b4e8cd7 | 2018-02-27 15:39:08 -0800 | [diff] [blame] | 53 | @AutoCodec.VisibleForSerialization |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 54 | TreeArtifactValue( |
| 55 | byte[] digest, ImmutableSortedMap<TreeFileArtifact, FileArtifactValue> childData) { |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 56 | this.digest = digest; |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 57 | this.childData = childData; |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | /** |
buchgr | 75bb7a9 | 2019-03-14 07:09:33 -0700 | [diff] [blame] | 61 | * Returns a TreeArtifactValue out of the given Artifact-relative path fragments and their |
| 62 | * corresponding FileArtifactValues. |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 63 | */ |
Rumou Duan | baeed33 | 2016-10-18 15:30:25 +0000 | [diff] [blame] | 64 | static TreeArtifactValue create(Map<TreeFileArtifact, FileArtifactValue> childFileValues) { |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 65 | Map<String, FileArtifactValue> digestBuilder = |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 66 | Maps.newHashMapWithExpectedSize(childFileValues.size()); |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 67 | for (Map.Entry<TreeFileArtifact, FileArtifactValue> e : childFileValues.entrySet()) { |
plf | 445260d | 2019-03-15 12:11:03 -0700 | [diff] [blame] | 68 | digestBuilder.put(e.getKey().getParentRelativePath().getPathString(), e.getValue()); |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 69 | } |
plf | 445260d | 2019-03-15 12:11:03 -0700 | [diff] [blame] | 70 | |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 71 | return new TreeArtifactValue( |
Shreya Bhattarai | 141b6c2 | 2016-08-22 22:00:24 +0000 | [diff] [blame] | 72 | DigestUtils.fromMetadata(digestBuilder).getDigestBytesUnsafe(), |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 73 | ImmutableSortedMap.copyOf(childFileValues)); |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 74 | } |
| 75 | |
Rumou Duan | baeed33 | 2016-10-18 15:30:25 +0000 | [diff] [blame] | 76 | FileArtifactValue getSelfData() { |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 77 | return FileArtifactValue.createProxy(digest); |
| 78 | } |
| 79 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 80 | FileArtifactValue getMetadata() { |
ulfjack | 01776ee | 2017-06-26 10:33:05 +0200 | [diff] [blame] | 81 | return getSelfData(); |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 82 | } |
| 83 | |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 84 | ImmutableSet<PathFragment> getChildPaths() { |
| 85 | return childData.keySet().stream() |
| 86 | .map(TreeFileArtifact::getParentRelativePath) |
| 87 | .collect(toImmutableSet()); |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | @Nullable |
Rumou Duan | baeed33 | 2016-10-18 15:30:25 +0000 | [diff] [blame] | 91 | byte[] getDigest() { |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 92 | return digest.clone(); |
| 93 | } |
| 94 | |
Rumou Duan | baeed33 | 2016-10-18 15:30:25 +0000 | [diff] [blame] | 95 | Iterable<TreeFileArtifact> getChildren() { |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 96 | return childData.keySet(); |
| 97 | } |
| 98 | |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 99 | ImmutableMap<TreeFileArtifact, FileArtifactValue> getChildValues() { |
Rumou Duan | baeed33 | 2016-10-18 15:30:25 +0000 | [diff] [blame] | 100 | return childData; |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 101 | } |
| 102 | |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 103 | @Override |
mjhalupka | 61020a7 | 2018-12-20 10:57:43 -0800 | [diff] [blame] | 104 | public BigInteger getValueFingerprint() { |
| 105 | if (valueFingerprint == null) { |
| 106 | BigIntegerFingerprint fp = new BigIntegerFingerprint(); |
janakr | 458f07a | 2019-02-05 13:51:54 -0800 | [diff] [blame] | 107 | fp.addDigestedBytes(digest); |
mjhalupka | 61020a7 | 2018-12-20 10:57:43 -0800 | [diff] [blame] | 108 | valueFingerprint = fp.getFingerprint(); |
| 109 | } |
| 110 | return valueFingerprint; |
| 111 | } |
| 112 | |
| 113 | @Override |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 114 | 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 Bhattarai | 3b09680 | 2016-08-17 23:14:52 +0000 | [diff] [blame] | 129 | if (!Arrays.equals(digest, that.digest)) { |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 130 | 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 | /** |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 145 | * A TreeArtifactValue that represents a missing TreeArtifact. This is occasionally useful because |
| 146 | * Java's concurrent collections disallow null members. |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 147 | */ |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 148 | static final TreeArtifactValue MISSING_TREE_ARTIFACT = |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 149 | new TreeArtifactValue(null, ImmutableSortedMap.of()) { |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 150 | @Override |
| 151 | FileArtifactValue getSelfData() { |
| 152 | throw new UnsupportedOperationException(); |
| 153 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 154 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 155 | @Override |
| 156 | Iterable<TreeFileArtifact> getChildren() { |
| 157 | throw new UnsupportedOperationException(); |
| 158 | } |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 159 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 160 | @Override |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 161 | ImmutableMap<TreeFileArtifact, FileArtifactValue> getChildValues() { |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 162 | throw new UnsupportedOperationException(); |
| 163 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 164 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 165 | @Override |
| 166 | FileArtifactValue getMetadata() { |
| 167 | throw new UnsupportedOperationException(); |
| 168 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 169 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 170 | @Override |
Googler | 9bd8833 | 2019-03-19 18:09:44 -0700 | [diff] [blame^] | 171 | ImmutableSet<PathFragment> getChildPaths() { |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 172 | throw new UnsupportedOperationException(); |
| 173 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 174 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 175 | @Nullable |
| 176 | @Override |
| 177 | byte[] getDigest() { |
| 178 | throw new UnsupportedOperationException(); |
| 179 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 180 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 181 | @Override |
| 182 | public int hashCode() { |
| 183 | return 24; // my favorite number |
| 184 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 185 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 186 | @Override |
| 187 | public boolean equals(Object other) { |
| 188 | return this == other; |
| 189 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 190 | |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 191 | @Override |
| 192 | public String toString() { |
| 193 | return "MISSING_TREE_ARTIFACT"; |
| 194 | } |
| 195 | }; |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 196 | |
felly | 09efb3f | 2018-07-26 07:46:15 -0700 | [diff] [blame] | 197 | private static void explodeDirectory(Path treeArtifactPath, |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 198 | PathFragment pathToExplode, ImmutableSet.Builder<PathFragment> valuesBuilder) |
Rumou Duan | 9ad28cd | 2016-10-19 19:28:06 +0000 | [diff] [blame] | 199 | throws IOException { |
Googler | feb2670 | 2018-11-06 10:23:45 -0800 | [diff] [blame] | 200 | 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 Duan | 11730a0 | 2016-10-24 20:27:58 +0000 | [diff] [blame] | 208 | PathFragment linkTarget = subpath.readSymbolicLinkUnchecked(); |
Janak Ramakrishnan | 6b4a8b3 | 2017-02-22 19:51:28 +0000 | [diff] [blame] | 209 | valuesBuilder.add(canonicalSubpathFragment); |
Rumou Duan | 11730a0 | 2016-10-24 20:27:58 +0000 | [diff] [blame] | 210 | if (linkTarget.isAbsolute()) { |
Janak Ramakrishnan | 6b4a8b3 | 2017-02-22 19:51:28 +0000 | [diff] [blame] | 211 | // 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 Duan | 11730a0 | 2016-10-24 20:27:58 +0000 | [diff] [blame] | 214 | } |
Rumou Duan | 11730a0 | 2016-10-24 20:27:58 +0000 | [diff] [blame] | 215 | // 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()) { |
tomlu | a729b9b | 2018-02-08 15:32:00 -0800 | [diff] [blame] | 222 | intermediatePath = intermediatePath.getRelative(pathSegment); |
Rumou Duan | 11730a0 | 2016-10-24 20:27:58 +0000 | [diff] [blame] | 223 | 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 | } |
Googler | feb2670 | 2018-11-06 10:23:45 -0800 | [diff] [blame] | 232 | } else if (dirent.getType() == Type.FILE) { |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 233 | valuesBuilder.add(canonicalSubpathFragment); |
| 234 | } else { |
| 235 | // We shouldn't ever reach here. |
Googler | feb2670 | 2018-11-06 10:23:45 -0800 | [diff] [blame] | 236 | Path subpath = dir.getRelative(dirent.getName()); |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 237 | throw new IllegalStateException("Could not determine type of file " + subpath); |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | /** |
lpino | 578ffcc | 2018-12-06 07:08:06 -0800 | [diff] [blame] | 243 | * 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 Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 248 | */ |
lpino | 578ffcc | 2018-12-06 07:08:06 -0800 | [diff] [blame] | 249 | public static Set<PathFragment> explodeDirectory(Path treeArtifactPath) throws IOException { |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 250 | ImmutableSet.Builder<PathFragment> explodedDirectory = ImmutableSet.builder(); |
felly | 09efb3f | 2018-07-26 07:46:15 -0700 | [diff] [blame] | 251 | explodeDirectory(treeArtifactPath, PathFragment.EMPTY_FRAGMENT, explodedDirectory); |
Michael Thvedt | e4a7b079 | 2016-02-09 12:15:53 +0000 | [diff] [blame] | 252 | return explodedDirectory.build(); |
| 253 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 254 | } |