GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/swissknife_reflog.cc
Date: 2026-08-30 02:40:36
Exec Total Coverage
Lines: 0 137 0.0%
Branches: 0 68 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "swissknife_reflog.h"
6
7 #include <cassert>
8 #include <string>
9 #include <vector>
10
11 #include "manifest.h"
12 #include "object_fetcher.h"
13 #include "upload_facility.h"
14 #include "util/exception.h"
15
16 namespace swissknife {
17
18 typedef HttpObjectFetcher<> ObjectFetcher;
19
20 class RootChainWalker {
21 public:
22 typedef ObjectFetcher::CatalogTN CatalogTN;
23 typedef ObjectFetcher::HistoryTN HistoryTN;
24
25 public:
26 RootChainWalker(const manifest::Manifest *manifest,
27 ObjectFetcher *object_fetcher,
28 manifest::Reflog *reflog)
29 : object_fetcher_(object_fetcher)
30 , reflog_(reflog)
31 , manifest_(manifest) { }
32
33 void FindObjectsAndPopulateReflog();
34
35 protected:
36 typedef std::vector<shash::Any> CatalogList;
37
38 protected:
39 CatalogTN *FetchCatalog(const shash::Any catalog_hash);
40 HistoryTN *FetchHistory(const shash::Any history_hash);
41
42 void WalkRootCatalogs(const shash::Any &root_catalog_hash);
43 void WalkHistories(const shash::Any &history_hash);
44
45 bool WalkCatalogsInHistory(const HistoryTN *history);
46 void WalkListedCatalogs(const CatalogList &catalog_list);
47
48 template<class DatabaseT>
49 DatabaseT *ReturnOrAbort(const ObjectFetcherFailures::Failures failure,
50 const shash::Any &content_hash,
51 DatabaseT *database);
52
53 private:
54 ObjectFetcher *object_fetcher_;
55 manifest::Reflog *reflog_;
56 const manifest::Manifest *manifest_;
57 };
58
59
60 ParameterList CommandReconstructReflog::GetParams() const {
61 ParameterList r;
62 r.push_back(Parameter::Mandatory('r', "repository url"));
63 r.push_back(Parameter::Mandatory('u', "spooler definition string"));
64 r.push_back(Parameter::Mandatory('n', "fully qualified repository name"));
65 r.push_back(Parameter::Mandatory('t', "temporary directory"));
66 r.push_back(Parameter::Mandatory('k', "repository keychain"));
67 r.push_back(Parameter::Mandatory('R', "path to reflog.chksum file"));
68 r.push_back(Parameter::Optional('@', "proxy url"));
69 return r;
70 }
71
72
73 int CommandReconstructReflog::Main(const ArgumentList &args) {
74 const std::string &repo_url = *args.find('r')->second;
75 const std::string &spooler = *args.find('u')->second;
76 const std::string &repo_name = *args.find('n')->second;
77 const std::string &tmp_dir = *args.find('t')->second;
78 const std::string &repo_keys = *args.find('k')->second;
79 const std::string &reflog_chksum_path = *args.find('R')->second;
80
81 const bool follow_redirects = false;
82 const std::string proxy = ((args.count('@') > 0) ? *args.find('@')->second
83 : "");
84 if (!this->InitDownloadManager(follow_redirects, proxy)
85 || !this->InitSignatureManager(repo_keys)) {
86 LogCvmfs(kLogCvmfs, kLogStderr, "failed to init repo connection");
87 return 1;
88 }
89
90 ObjectFetcher object_fetcher(repo_name, repo_url, tmp_dir, download_manager(),
91 signature_manager());
92
93 std::unique_ptr<manifest::Manifest> manifest;
94 const ObjectFetcher::Failures retval = object_fetcher.FetchManifest(
95 &manifest);
96 if (retval != ObjectFetcher::kFailOk) {
97 LogCvmfs(kLogCvmfs, kLogStderr,
98 "failed to load repository manifest "
99 "(%d - %s)",
100 retval, Code2Ascii(retval));
101 return 1;
102 }
103
104 const upload::SpoolerDefinition spooler_definition(spooler, shash::kAny);
105 const std::unique_ptr<upload::AbstractUploader> uploader(
106 upload::AbstractUploader::Construct(spooler_definition));
107
108 if (uploader.get() == nullptr) {
109 LogCvmfs(kLogCvmfs, kLogStderr, "failed to initialize spooler for '%s'",
110 spooler.c_str());
111 return 1;
112 }
113
114 std::unique_ptr<manifest::Reflog> reflog(
115 CreateEmptyReflog(tmp_dir, repo_name));
116 reflog->TakeDatabaseFileOwnership();
117
118 reflog->BeginTransaction();
119 AddStaticManifestObjects(reflog.get(), manifest.get());
120 RootChainWalker walker(manifest.get(), &object_fetcher, reflog.get());
121 walker.FindObjectsAndPopulateReflog();
122 reflog->CommitTransaction();
123
124 LogCvmfs(kLogCvmfs, kLogStdout, "found %lu entries", reflog->CountEntries());
125
126 reflog->DropDatabaseFileOwnership();
127 const std::string reflog_db = reflog->database_file();
128 reflog.reset();
129 uploader->UploadFile(reflog_db, ".cvmfsreflog");
130 shash::Any reflog_hash(manifest->GetHashAlgorithm());
131 manifest::Reflog::HashDatabase(reflog_db, &reflog_hash);
132 uploader->WaitForUpload();
133 unlink(reflog_db.c_str());
134
135 const int errors = uploader->GetNumberOfErrors();
136 if (errors > 0) {
137 LogCvmfs(kLogCvmfs, kLogStderr, "failed to upload generated Reflog");
138 }
139
140 uploader->TearDown();
141
142 manifest::Reflog::WriteChecksum(reflog_chksum_path, reflog_hash);
143
144 return (errors == 0) ? 0 : 1;
145 }
146
147
148 void CommandReconstructReflog::AddStaticManifestObjects(
149 manifest::Reflog *reflog, manifest::Manifest *manifest) const {
150 const shash::Any certificate = manifest->certificate();
151 const shash::Any meta_info = manifest->meta_info();
152 assert(!certificate.IsNull());
153
154 bool success = reflog->AddCertificate(certificate);
155 assert(success);
156 LogCvmfs(kLogCvmfs, kLogStdout, "Certificate: %s",
157 certificate.ToString().c_str());
158
159 if (!meta_info.IsNull()) {
160 success = reflog->AddMetainfo(meta_info);
161 assert(success);
162 LogCvmfs(kLogCvmfs, kLogStdout, "Metainfo: %s",
163 meta_info.ToString().c_str());
164 }
165 }
166
167
168 void RootChainWalker::FindObjectsAndPopulateReflog() {
169 const shash::Any root_catalog = manifest_->catalog_hash();
170 const shash::Any history = manifest_->history();
171
172 assert(!root_catalog.IsNull());
173 WalkRootCatalogs(root_catalog);
174
175 if (!history.IsNull()) {
176 WalkHistories(history);
177 }
178 }
179
180
181 void RootChainWalker::WalkRootCatalogs(const shash::Any &root_catalog_hash) {
182 shash::Any current_hash = root_catalog_hash;
183 std::unique_ptr<CatalogTN> current_catalog;
184
185 while (!current_hash.IsNull() && !reflog_->ContainsCatalog(current_hash)
186 && (current_catalog = std::unique_ptr<CatalogTN>(
187 FetchCatalog(current_hash)))
188 .get()
189 != nullptr) {
190 LogCvmfs(kLogCvmfs, kLogStdout, "Catalog: %s Revision: %" PRIu64,
191 current_hash.ToString().c_str(), current_catalog->GetRevision());
192
193 const bool success = reflog_->AddCatalog(current_hash);
194 assert(success);
195
196 current_hash = current_catalog->GetPreviousRevision();
197 }
198 }
199
200
201 void RootChainWalker::WalkHistories(const shash::Any &history_hash) {
202 shash::Any current_hash = history_hash;
203 std::unique_ptr<HistoryTN> current_history;
204
205 while (!current_hash.IsNull() && !reflog_->ContainsHistory(current_hash)
206 && (current_history = std::unique_ptr<HistoryTN>(
207 FetchHistory(current_hash)))
208 .get()
209 != nullptr) {
210 LogCvmfs(kLogCvmfs, kLogStdout, "History: %s",
211 current_hash.ToString().c_str());
212
213 const bool cancel = WalkCatalogsInHistory(current_history.get());
214 const bool success = reflog_->AddHistory(current_hash);
215 assert(success);
216
217 if (cancel) {
218 current_hash = shash::Any(current_hash.algorithm);
219 } else {
220 current_hash = current_history->previous_revision();
221 }
222 }
223 }
224
225
226 /**
227 * If false is returned, it indicates that the history database comes from
228 * an early pre-release of the history functionality. The WalkHistory()
229 * iteration will subsequently stop. This is necessary, for instance, to
230 * handle the cernvm-prod.cern.ch repository.
231 */
232 bool RootChainWalker::WalkCatalogsInHistory(const HistoryTN *history) {
233 CatalogList tag_hashes;
234 const bool list_success = history->GetHashes(&tag_hashes);
235 assert(list_success);
236
237 CatalogList bin_hashes;
238 const bool bin_success = history->ListRecycleBin(&bin_hashes);
239 if (!bin_success) {
240 LogCvmfs(kLogCvmfs, kLogStderr, " Warning: 'recycle bin' table missing");
241 }
242
243 WalkListedCatalogs(tag_hashes);
244 WalkListedCatalogs(bin_hashes);
245
246 return !bin_success;
247 }
248
249
250 void RootChainWalker::WalkListedCatalogs(
251 const RootChainWalker::CatalogList &catalog_list) {
252 CatalogList::const_iterator i = catalog_list.begin();
253 const CatalogList::const_iterator iend = catalog_list.end();
254 for (; i != iend; ++i) {
255 WalkRootCatalogs(*i);
256 }
257 }
258
259
260 RootChainWalker::CatalogTN *RootChainWalker::FetchCatalog(
261 const shash::Any catalog_hash) {
262 CatalogTN *catalog = NULL;
263 const char *root_path = "";
264 const ObjectFetcherFailures::Failures failure = object_fetcher_->FetchCatalog(
265 catalog_hash, root_path, &catalog);
266
267 return ReturnOrAbort(failure, catalog_hash, catalog);
268 }
269
270
271 RootChainWalker::HistoryTN *RootChainWalker::FetchHistory(
272 const shash::Any history_hash) {
273 HistoryTN *history = NULL;
274 const ObjectFetcherFailures::Failures failure = object_fetcher_->FetchHistory(
275 &history, history_hash);
276
277 return ReturnOrAbort(failure, history_hash, history);
278 }
279
280
281 template<class DatabaseT>
282 DatabaseT *RootChainWalker::ReturnOrAbort(
283 const ObjectFetcherFailures::Failures failure,
284 const shash::Any &content_hash,
285 DatabaseT *database) {
286 switch (failure) {
287 case ObjectFetcherFailures::kFailOk:
288 return database;
289 case ObjectFetcherFailures::kFailNotFound:
290 return NULL;
291 default:
292 PANIC(kLogStderr, "Failed to load object '%s' (%d - %s)",
293 content_hash.ToStringWithSuffix().c_str(), failure,
294 Code2Ascii(failure));
295 }
296 }
297
298 } // namespace swissknife
299