GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/swissknife_reflog.cc
Date: 2025-07-13 02:35:07
Exec Total Coverage
Lines: 0 134 0.0%
Branches: 0 64 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 UniquePtr<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 UniquePtr<upload::AbstractUploader> uploader(
106 upload::AbstractUploader::Construct(spooler_definition));
107
108 if (!uploader.IsValid()) {
109 LogCvmfs(kLogCvmfs, kLogStderr, "failed to initialize spooler for '%s'",
110 spooler.c_str());
111 return 1;
112 }
113
114 UniquePtr<manifest::Reflog> reflog(CreateEmptyReflog(tmp_dir, repo_name));
115 reflog->TakeDatabaseFileOwnership();
116
117 reflog->BeginTransaction();
118 AddStaticManifestObjects(reflog.weak_ref(), manifest.weak_ref());
119 RootChainWalker walker(manifest.weak_ref(), &object_fetcher,
120 reflog.weak_ref());
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.Destroy();
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 UniquePtr<CatalogTN> current_catalog;
184
185 while (!current_hash.IsNull() && !reflog_->ContainsCatalog(current_hash)
186 && (current_catalog = FetchCatalog(current_hash)).IsValid()) {
187 LogCvmfs(kLogCvmfs, kLogStdout, "Catalog: %s Revision: %" PRIu64,
188 current_hash.ToString().c_str(), current_catalog->GetRevision());
189
190 const bool success = reflog_->AddCatalog(current_hash);
191 assert(success);
192
193 current_hash = current_catalog->GetPreviousRevision();
194 }
195 }
196
197
198 void RootChainWalker::WalkHistories(const shash::Any &history_hash) {
199 shash::Any current_hash = history_hash;
200 UniquePtr<HistoryTN> current_history;
201
202 while (!current_hash.IsNull() && !reflog_->ContainsHistory(current_hash)
203 && (current_history = FetchHistory(current_hash)).IsValid()) {
204 LogCvmfs(kLogCvmfs, kLogStdout, "History: %s",
205 current_hash.ToString().c_str());
206
207 const bool cancel = WalkCatalogsInHistory(current_history.weak_ref());
208 const bool success = reflog_->AddHistory(current_hash);
209 assert(success);
210
211 if (cancel) {
212 current_hash = shash::Any(current_hash.algorithm);
213 } else {
214 current_hash = current_history->previous_revision();
215 }
216 }
217 }
218
219
220 /**
221 * If false is returned, it indicates that the history database comes from
222 * an early pre-release of the history functionality. The WalkHistory()
223 * iteration will subsequently stop. This is necessary, for instance, to
224 * handle the cernvm-prod.cern.ch repository.
225 */
226 bool RootChainWalker::WalkCatalogsInHistory(const HistoryTN *history) {
227 CatalogList tag_hashes;
228 const bool list_success = history->GetHashes(&tag_hashes);
229 assert(list_success);
230
231 CatalogList bin_hashes;
232 const bool bin_success = history->ListRecycleBin(&bin_hashes);
233 if (!bin_success) {
234 LogCvmfs(kLogCvmfs, kLogStderr, " Warning: 'recycle bin' table missing");
235 }
236
237 WalkListedCatalogs(tag_hashes);
238 WalkListedCatalogs(bin_hashes);
239
240 return !bin_success;
241 }
242
243
244 void RootChainWalker::WalkListedCatalogs(
245 const RootChainWalker::CatalogList &catalog_list) {
246 CatalogList::const_iterator i = catalog_list.begin();
247 const CatalogList::const_iterator iend = catalog_list.end();
248 for (; i != iend; ++i) {
249 WalkRootCatalogs(*i);
250 }
251 }
252
253
254 RootChainWalker::CatalogTN *RootChainWalker::FetchCatalog(
255 const shash::Any catalog_hash) {
256 CatalogTN *catalog = NULL;
257 const char *root_path = "";
258 const ObjectFetcherFailures::Failures failure = object_fetcher_->FetchCatalog(
259 catalog_hash, root_path, &catalog);
260
261 return ReturnOrAbort(failure, catalog_hash, catalog);
262 }
263
264
265 RootChainWalker::HistoryTN *RootChainWalker::FetchHistory(
266 const shash::Any history_hash) {
267 HistoryTN *history = NULL;
268 const ObjectFetcherFailures::Failures failure = object_fetcher_->FetchHistory(
269 &history, history_hash);
270
271 return ReturnOrAbort(failure, history_hash, history);
272 }
273
274
275 template<class DatabaseT>
276 DatabaseT *RootChainWalker::ReturnOrAbort(
277 const ObjectFetcherFailures::Failures failure,
278 const shash::Any &content_hash,
279 DatabaseT *database) {
280 switch (failure) {
281 case ObjectFetcherFailures::kFailOk:
282 return database;
283 case ObjectFetcherFailures::kFailNotFound:
284 return NULL;
285 default:
286 PANIC(kLogStderr, "Failed to load object '%s' (%d - %s)",
287 content_hash.ToStringWithSuffix().c_str(), failure,
288 Code2Ascii(failure));
289 }
290 }
291
292 } // namespace swissknife
293