diff options
author | Sascha Roloff <sascha.roloff@huawei.com> | 2024-01-17 16:42:49 +0100 |
---|---|---|
committer | Sascha Roloff <sascha.roloff@huawei.com> | 2024-01-19 18:06:05 +0100 |
commit | 399f75c8b63c3214938e03d5d9b532a5dd7a4000 (patch) | |
tree | bedf0626534c050469f3aa26ae68d12e8604d150 /src/buildtool/execution_api/execution_service/cas_server.cpp | |
parent | f410e394b48d05e06cf3c5fefe93a91d96c4fbb6 (diff) | |
download | justbuild-399f75c8b63c3214938e03d5d9b532a5dd7a4000.tar.gz |
Add tree invariant check for just execute, when uploading trees
Diffstat (limited to 'src/buildtool/execution_api/execution_service/cas_server.cpp')
-rw-r--r-- | src/buildtool/execution_api/execution_service/cas_server.cpp | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/src/buildtool/execution_api/execution_service/cas_server.cpp b/src/buildtool/execution_api/execution_service/cas_server.cpp index ce3feabb..e68092f8 100644 --- a/src/buildtool/execution_api/execution_service/cas_server.cpp +++ b/src/buildtool/execution_api/execution_service/cas_server.cpp @@ -21,9 +21,15 @@ #include <vector> #include "fmt/core.h" +#include "src/buildtool/common/artifact_digest.hpp" +#include "src/buildtool/compatibility/compatibility.hpp" #include "src/buildtool/compatibility/native_support.hpp" #include "src/buildtool/execution_api/execution_service/file_chunker.hpp" +#include "src/buildtool/file_system/git_repo.hpp" +#include "src/buildtool/file_system/object_type.hpp" +#include "src/buildtool/logging/log_level.hpp" #include "src/buildtool/storage/garbage_collector.hpp" +#include "src/utils/cpp/hex_string.hpp" #include "src/utils/cpp/verify_hash.hpp" static constexpr std::size_t kJustHashLength = 42; @@ -94,6 +100,46 @@ auto CASServiceImpl::CheckDigestConsistency(bazel_re::Digest const& ref, return std::nullopt; } +auto CASServiceImpl::EnsureTreeInvariant(std::string const& data, + std::string const& hash) const noexcept + -> std::optional<std::string> { + auto entries = GitRepo::ReadTreeData( + data, + NativeSupport::Unprefix(hash), + [](auto const& /*unused*/) { return true; }, + /*is_hex_id=*/true); + if (not entries) { + auto str = fmt::format("Could not read tree data {}", hash); + logger_.Emit(LogLevel::Error, str); + return str; + } + for (auto const& entry : *entries) { + for (auto const& item : entry.second) { + auto digest = static_cast<bazel_re::Digest>( + ArtifactDigest{ToHexString(entry.first), + /*size is unknown*/ 0, + IsTreeObject(item.type)}); + if (not(IsTreeObject(item.type) + ? storage_->CAS().TreePath(digest) + : storage_->CAS().BlobPath(digest, false))) { + auto str = fmt::format( + "Tree invariant violated {}: missing element {}", + hash, + digest.hash()); + logger_.Emit(LogLevel::Error, str); + return str; + } + // The GitRepo::tree_entries_t data structure maps the object id to + // a list of entries of that object in possibly multiple trees. It + // is sufficient to check the existence of only one of these entries + // to be sure that the object is in CAS since they all have the same + // content. + break; + } + } + return std::nullopt; +} + auto CASServiceImpl::BatchUpdateBlobs( ::grpc::ServerContext* /*context*/, const ::bazel_re::BatchUpdateBlobsRequest* request, @@ -118,6 +164,12 @@ auto CASServiceImpl::BatchUpdateBlobs( auto* r = response->add_responses(); r->mutable_digest()->CopyFrom(x.digest()); if (NativeSupport::IsTree(hash)) { + // In native mode: for trees, check whether the tree invariant holds + // before storing the actual tree object. + if (auto err = EnsureTreeInvariant(x.data(), hash)) { + return ::grpc::Status{grpc::StatusCode::FAILED_PRECONDITION, + *err}; + } auto const& dgst = storage_->CAS().StoreTree(x.data()); if (!dgst) { auto const& str = fmt::format( |