summaryrefslogtreecommitdiff
path: root/src/buildtool/execution_api/local/local_storage.cpp
blob: c007fc1dd87d314e158b1f5589ebe9e55e60034d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "src/buildtool/execution_api/local/local_api.hpp"

namespace {

[[nodiscard]] auto ReadDirectory(
    gsl::not_null<LocalStorage const*> const& storage,
    bazel_re::Digest const& digest) noexcept
    -> std::optional<bazel_re::Directory> {
    if (auto const path = storage->BlobPath(digest, /*is_executable=*/false)) {
        if (auto const content = FileSystemManager::ReadFile(*path)) {
            return BazelMsgFactory::MessageFromString<bazel_re::Directory>(
                *content);
        }
    }
    Logger::Log(
        LogLevel::Error, "Directory {} not found in CAS", digest.hash());
    return std::nullopt;
}

[[nodiscard]] auto TreeToStream(
    gsl::not_null<LocalStorage const*> const& storage,
    bazel_re::Digest const& tree_digest,
    gsl::not_null<FILE*> const& stream) noexcept -> bool {
    if (auto dir = ReadDirectory(storage, tree_digest)) {
        if (auto data = BazelMsgFactory::DirectoryToString(*dir)) {
            std::fwrite(data->data(), 1, data->size(), stream);
            return true;
        }
    }
    return false;
}

[[nodiscard]] auto BlobToStream(
    gsl::not_null<LocalStorage const*> const& storage,
    Artifact::ObjectInfo const& blob_info,
    gsl::not_null<FILE*> const& stream) noexcept -> bool {
    constexpr std::size_t kChunkSize{512};
    if (auto const path = storage->BlobPath(
            blob_info.digest, IsExecutableObject(blob_info.type))) {
        std::string data(kChunkSize, '\0');
        if (gsl::owner<FILE*> in = std::fopen(path->c_str(), "rb")) {
            while (auto size = std::fread(data.data(), 1, kChunkSize, in)) {
                std::fwrite(data.data(), 1, size, stream);
            }
            std::fclose(in);
            return true;
        }
    }
    return false;
}

}  // namespace

auto LocalStorage::ReadTreeInfos(
    bazel_re::Digest const& tree_digest,
    std::filesystem::path const& parent) const noexcept
    -> std::optional<std::pair<std::vector<std::filesystem::path>,
                               std::vector<Artifact::ObjectInfo>>> {
    std::vector<std::filesystem::path> paths{};
    std::vector<Artifact::ObjectInfo> infos{};

    auto store_info = [&paths, &infos](auto path, auto info) {
        paths.emplace_back(path);
        infos.emplace_back(info);
        return true;
    };

    if (ReadObjectInfosRecursively(store_info, parent, tree_digest)) {
        return std::make_pair(std::move(paths), std::move(infos));
    }
    return std::nullopt;
}

auto LocalStorage::ReadObjectInfosRecursively(
    BazelMsgFactory::InfoStoreFunc const& store_info,
    std::filesystem::path const& parent,
    bazel_re::Digest const& digest) const noexcept -> bool {
    // read from in-memory tree map
    if (tree_map_) {
        auto const* tree = tree_map_->GetTree(digest);
        if (tree != nullptr) {
            for (auto const& [path, info] : *tree) {
                try {
                    if (IsTreeObject(info->type)
                            ? not ReadObjectInfosRecursively(
                                  store_info, parent / path, info->digest)
                            : not store_info(parent / path, *info)) {
                        return false;
                    }
                } catch (...) {  // satisfy clang-tidy, store_info() could throw
                    return false;
                }
            }
            return true;
        }
        Logger::Log(
            LogLevel::Debug, "tree {} not found in tree map", digest.hash());
    }

    // fallback read from CAS and cache it in in-memory tree map
    if (auto dir = ReadDirectory(this, digest)) {
        auto tree = tree_map_ ? std::make_optional(tree_map_->CreateTree())
                              : std::nullopt;
        return BazelMsgFactory::ReadObjectInfosFromDirectory(
                   *dir,
                   [this, &store_info, &parent, &tree](auto path, auto info) {
                       return (not tree or tree->AddInfo(path, info)) and
                              (IsTreeObject(info.type)
                                   ? ReadObjectInfosRecursively(
                                         store_info, parent / path, info.digest)
                                   : store_info(parent / path, info));
                   }) and
               (not tree_map_ or tree_map_->AddTree(digest, std::move(*tree)));
    }
    return false;
}

auto LocalStorage::DumpToStream(
    Artifact::ObjectInfo const& info,
    gsl::not_null<FILE*> const& stream) const noexcept -> bool {
    return IsTreeObject(info.type) ? TreeToStream(this, info.digest, stream)
                                   : BlobToStream(this, info, stream);
}