Skip to content

Commit 2c9cf49

Browse files
committed
Add a FetchedArchive type representing an archive pulled by cvd fetch
Bug: b/463470215
1 parent a3f4d67 commit 2c9cf49

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

base/cvd/cuttlefish/host/libs/config/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,23 @@ cf_cc_library(
184184
],
185185
)
186186

187+
cf_cc_library(
188+
name = "fetched_archive",
189+
srcs = ["fetched_archive.cc"],
190+
hdrs = ["fetched_archive.h"],
191+
deps = [
192+
"//cuttlefish/common/libs/utils:files",
193+
"//cuttlefish/common/libs/utils:result",
194+
"//cuttlefish/host/libs/config:fetcher_config",
195+
"//cuttlefish/host/libs/config:file_source",
196+
"//cuttlefish/host/libs/zip:zip_file",
197+
"//cuttlefish/host/libs/zip/libzip_cc:archive",
198+
"@abseil-cpp//absl/strings",
199+
"@abseil-cpp//absl/strings:str_format",
200+
"@fmt",
201+
],
202+
)
203+
187204
cf_cc_library(
188205
name = "fetcher_config",
189206
srcs = ["fetcher_config.cpp"],
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//
2+
// Copyright (C) 2025 The Android Open Source Project
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
#include "cuttlefish/host/libs/config/fetched_archive.h"
17+
18+
#include <stddef.h>
19+
20+
#include <map>
21+
#include <optional>
22+
#include <ostream>
23+
#include <set>
24+
#include <string>
25+
#include <string_view>
26+
#include <utility>
27+
28+
#include "absl/strings/match.h"
29+
#include "absl/strings/str_cat.h"
30+
#include "absl/strings/strip.h"
31+
#include "fmt/ostream.h"
32+
#include "fmt/ranges.h"
33+
34+
#include "cuttlefish/common/libs/utils/files.h"
35+
#include "cuttlefish/common/libs/utils/result.h"
36+
#include "cuttlefish/host/libs/config/fetcher_config.h"
37+
#include "cuttlefish/host/libs/config/file_source.h"
38+
#include "cuttlefish/host/libs/zip/libzip_cc/archive.h"
39+
#include "cuttlefish/host/libs/zip/zip_file.h"
40+
41+
namespace cuttlefish {
42+
43+
Result<FetchedArchive> FetchedArchive::Create(
44+
const FetcherConfig& fetcher_config, FileSource source,
45+
std::string_view archive) {
46+
std::optional<ReadableZip> zip_file;
47+
std::set<std::string> members;
48+
std::map<std::string, std::string> extracted_members;
49+
50+
// To validate `xyz.zip` only has exact matches and not `/abc-xyz.zip`.
51+
std::string slash_archive = absl::StrCat("/", archive);
52+
for (const auto& [path, member] : fetcher_config.get_cvd_files()) {
53+
if (member.source != source) {
54+
continue;
55+
}
56+
57+
bool name_matches = path == archive || absl::EndsWith(path, slash_archive);
58+
if (name_matches && absl::EndsWith(archive, ".zip")) {
59+
zip_file = CF_EXPECT(ZipOpenRead(path));
60+
continue;
61+
}
62+
if (member.archive_source != archive) {
63+
continue;
64+
}
65+
66+
std::string_view archive_path = member.archive_path;
67+
while (absl::ConsumePrefix(&archive_path, "/")) {
68+
}
69+
70+
CF_EXPECTF(FileExists(path),
71+
"'{}' is present in the fetcher config not in the filesystem.",
72+
path);
73+
74+
members.insert(std::string(archive_path));
75+
extracted_members.emplace(std::string(archive_path), path);
76+
}
77+
78+
if (zip_file.has_value()) {
79+
size_t zip_entries = CF_EXPECT(zip_file->NumEntries());
80+
for (size_t i = 0; i < zip_entries; i++) {
81+
members.insert(CF_EXPECT(zip_file->EntryName(i)));
82+
}
83+
}
84+
85+
return FetchedArchive(source, std::move(extracted_members),
86+
std::move(members), std::move(zip_file));
87+
}
88+
89+
FetchedArchive::FetchedArchive(
90+
FileSource source, std::map<std::string, std::string> extracted_members,
91+
std::set<std::string> members, std::optional<ReadableZip> zip_file)
92+
: source_(source),
93+
extracted_members_(std::move(extracted_members)),
94+
members_(std::move(members)),
95+
zip_file_(std::move(zip_file)) {}
96+
97+
const std::set<std::string>& FetchedArchive::Members() const {
98+
return members_;
99+
}
100+
101+
Result<std::string> FetchedArchive::MemberFilepath(
102+
std::string_view member_name, std::optional<std::string_view> extract_dir) {
103+
return CF_ERR("TODO: schuffelen");
104+
}
105+
106+
std::ostream& operator<<(std::ostream& out,
107+
const FetchedArchive& fetched_archive) {
108+
out << "FetchedArchive {\n";
109+
fmt::print(out, "\tsource: '{}'\n",
110+
SourceEnumToString(fetched_archive.source_));
111+
fmt::print(out, "\textracted_members: [{}]\n",
112+
fmt::join(fetched_archive.extracted_members_, ", "));
113+
fmt::print(out, "\tmembers: [{}]\n",
114+
fmt::join(fetched_archive.members_, ", "));
115+
bool has_zip = fetched_archive.zip_file_.has_value();
116+
fmt::print(out, "\tzip: {}\n", has_zip ? "present" : "missing");
117+
return out << "}";
118+
}
119+
120+
} // namespace cuttlefish
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// Copyright (C) 2025 The Android Open Source Project
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
#pragma once
17+
18+
#include <map>
19+
#include <optional>
20+
#include <ostream>
21+
#include <set>
22+
#include <string>
23+
#include <string_view>
24+
25+
#include "absl/strings/str_format.h"
26+
#include "cuttlefish/host/libs/zip/libzip_cc/archive.h"
27+
#include "fmt/ostream.h"
28+
29+
#include "cuttlefish/common/libs/utils/result.h"
30+
#include "cuttlefish/host/libs/config/fetcher_config.h"
31+
#include "cuttlefish/host/libs/config/file_source.h"
32+
33+
namespace cuttlefish {
34+
35+
/**
36+
* An archive that was downloaded by `cvd fetch`.
37+
*
38+
* The archive may be partially or completely extracted, and the archive may
39+
* have been deleted as part of the fetch process, leaving only extracted files.
40+
*/
41+
class FetchedArchive {
42+
public:
43+
static Result<FetchedArchive> Create(const FetcherConfig&, FileSource,
44+
std::string_view archive_name);
45+
46+
template <typename Sink>
47+
friend void AbslStringify(Sink& sink, const FetchedArchive& img) {
48+
sink.Append(absl::FormatStreamed(img));
49+
}
50+
51+
/**
52+
* Returns the filenames of the members held in the archive.
53+
*
54+
* If a subset of the archive members were extracted and the archive was
55+
* deleted, this may be incomplete.
56+
*/
57+
const std::set<std::string>& Members() const;
58+
59+
/**
60+
* Returns the file path to a member of the archive, extracted on the
61+
* filesystem. If the member is not already extracted and the archive is
62+
* present, it will extract the file into `extract_dir`.
63+
*
64+
* Error conditions:
65+
*
66+
* - The archive does not have a member called `member_name`.
67+
* - The file needed to be extracted, but `extract_dir` was not present.
68+
* - There was a failure to extract the file member.
69+
*/
70+
Result<std::string> MemberFilepath(
71+
std::string_view member_name,
72+
std::optional<std::string_view> extract_dir);
73+
74+
friend std::ostream& operator<<(std::ostream&, const FetchedArchive&);
75+
76+
private:
77+
FetchedArchive(FileSource, std::map<std::string, std::string>,
78+
std::set<std::string>, std::optional<ReadableZip>);
79+
80+
FileSource source_;
81+
std::map<std::string, std::string> extracted_members_;
82+
std::set<std::string> members_;
83+
std::optional<ReadableZip> zip_file_;
84+
};
85+
86+
} // namespace cuttlefish
87+
88+
namespace fmt {
89+
90+
template <>
91+
struct formatter<::cuttlefish::FetchedArchive> : ostream_formatter {};
92+
93+
} // namespace fmt

0 commit comments

Comments
 (0)