GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/swissknife_list_reflog.cc
Date: 2026-09-27 02:40:09
Exec Total Coverage
Lines: 0 114 0.0%
Branches: 0 88 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * This command processes a repository's catalog structure to detect and remove
5 * outdated and/or unneeded data objects.
6 */
7
8
9 #include "swissknife_list_reflog.h"
10
11 #include "manifest.h"
12 #include "object_fetcher.h"
13 #include "reflog.h"
14 #include "util/posix.h"
15 #include "util/string.h"
16
17 using namespace std; // NOLINT
18
19 namespace swissknife {
20
21 ✗ ParameterList CommandListReflog::GetParams() const {
22 ✗ ParameterList r;
23 ✗ r.push_back(Parameter::Mandatory('r', "repository url / local storage path"));
24 ✗ r.push_back(Parameter::Mandatory('n', "fully qualified repository name"));
25 ✗ r.push_back(Parameter::Optional('R', "path to reflog.chksum file"));
26 ✗ r.push_back(Parameter::Optional('k', "repository master key(s) / dir"));
27 ✗ r.push_back(Parameter::Optional('t', "temporary directory"));
28 ✗ r.push_back(Parameter::Optional('o', "output file"));
29 ✗ r.push_back(Parameter::Optional('@', "proxy url"));
30 ✗ return r;
31 }
32
33 ✗ int CommandListReflog::Main(const ArgumentList &args) {
34 ✗ const string &repo_url = *args.find('r')->second;
35 ✗ const string &repo_name = *args.find('n')->second;
36 ✗ const std::string &reflog_chksum_path = (args.count('R') > 0)
37 ✗ ? *args.find('R')->second
38 ✗ : "";
39 ✗ string repo_keys = (args.count('k') > 0) ? *args.find('k')->second : "";
40 ✗ if (DirectoryExists(repo_keys))
41 ✗ repo_keys = JoinStrings(FindFilesBySuffix(repo_keys, ".pub"), ":");
42 ✗ const string temp_directory = (args.count('t') > 0) ? *args.find('t')->second
43 ✗ : "/tmp";
44 ✗ const string output_path = (args.count('o') > 0) ? *args.find('o')->second
45 ✗ : "";
46
47 ✗ shash::Any reflog_hash;
48 ✗ if (reflog_chksum_path != "") {
49 ✗ if (!manifest::Reflog::ReadChecksum(reflog_chksum_path, &reflog_hash)) {
50 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Could not read reflog checksum");
51 ✗ return 1;
52 }
53 }
54
55 ✗ const bool follow_redirects = false;
56 ✗ const string proxy = (args.count('@') > 0) ? *args.find('@')->second : "";
57 ✗ if (!this->InitDownloadManager(follow_redirects, proxy)
58 ✗ || !this->InitSignatureManager(repo_keys)) {
59 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "failed to init repo connection");
60 ✗ return 1;
61 }
62
63 bool success;
64 ✗ if (IsHttpUrl(repo_url)) {
65 HttpObjectFetcher<> object_fetcher(repo_name,
66 repo_url,
67 temp_directory,
68 download_manager(),
69 ✗ signature_manager());
70 ✗ if (reflog_hash.IsNull()) {
71 ✗ manifest::Manifest *manifest = NULL;
72 ObjectFetcherFailures::Failures failure;
73 ✗ switch (failure = object_fetcher.FetchManifest(&manifest)) {
74 ✗ case ObjectFetcherFailures::kFailOk:
75 ✗ reflog_hash = manifest->reflog_hash();
76 ✗ break;
77 ✗ default:
78 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Failed to fetch manifest: %s",
79 Code2Ascii(failure));
80 ✗ return 1;
81 }
82 ✗ delete manifest;
83 }
84 ✗ success = Run(&object_fetcher, repo_name, output_path, reflog_hash);
85 ✗ } else {
86 ✗ LocalObjectFetcher<> object_fetcher(repo_url, temp_directory);
87 ✗ success = Run(&object_fetcher, repo_name, output_path, reflog_hash);
88 }
89
90 ✗ return (success) ? 0 : 1;
91 }
92
93 template<class ObjectFetcherT>
94 ✗ bool CommandListReflog::Run(ObjectFetcherT *object_fetcher, string repo_name,
95 string output_path, shash::Any reflog_hash) {
96 typename ObjectFetcherT::ReflogTN *reflog;
97 ✗ reflog = FetchReflog(object_fetcher, repo_name, reflog_hash);
98
99 ✗ const shash::Any null_hash = shash::Any(reflog_hash.algorithm);
100 ✗ objects_.reset(new SmallHashDynamic<shash::Any, bool>);
101 ✗ objects_->Init(1024, null_hash, hasher);
102
103 // Traverse through catalogs and regular objects
104 ✗ vector<shash::Any> catalogs;
105 ✗ if (NULL == reflog || !reflog->List(SqlReflog::kRefCatalog, &catalogs)) {
106 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Failed to list catalog reference log");
107 ✗ return false;
108 }
109 ✗ typename CatalogTraversal<ObjectFetcherT>::Parameters traversal_params;
110 ✗ traversal_params.object_fetcher = object_fetcher;
111 ✗ CatalogTraversal<ObjectFetcherT> traversal(traversal_params);
112 ✗ traversal.RegisterListener(&swissknife::CommandListReflog::CatalogCallback,
113 this);
114 ✗ bool success = true;
115 ✗ vector<shash::Any>::iterator i = catalogs.begin();
116 ✗ const vector<shash::Any>::const_iterator iend = catalogs.end();
117 ✗ for (; i != iend && success; i++) {
118 ✗ success &= traversal.TraverseRevision(
119 ✗ *i, CatalogTraversal<ObjectFetcherT>::kBreadthFirst);
120 }
121
122 ✗ if (!success) {
123 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
124 "Catalog traversal aborted due to an error");
125 ✗ return false;
126 }
127
128 // Add history, certificate, metainfo objects from reflog
129 ✗ vector<shash::Any> histories, certificates, metainfos;
130 ✗ if (!reflog->List(SqlReflog::kRefHistory, &histories)) {
131 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
132 "Failed to fetch history objects from reflog");
133 ✗ return false;
134 }
135 ✗ if (!reflog->List(SqlReflog::kRefCertificate, &certificates)) {
136 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
137 "Failed to fetch certificate objects from reflog");
138 ✗ return false;
139 }
140 ✗ if (!reflog->List(SqlReflog::kRefMetainfo, &metainfos)) {
141 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
142 "Failed to fetch metainfo objects from reflog");
143 ✗ return false;
144 }
145 ✗ InsertObjects(histories);
146 ✗ InsertObjects(certificates);
147 ✗ InsertObjects(metainfos);
148
149 // Clean up reflog file
150 ✗ delete reflog;
151
152 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Number of objects: %u", objects_->size());
153
154 ✗ if (output_path == "") {
155 ✗ DumpObjects(stdout);
156 } else {
157 ✗ const int fd = open(output_path.c_str(), O_WRONLY | O_CREAT, 0644);
158 ✗ assert(fd);
159 ✗ FILE *stream = fdopen(fd, "w");
160 ✗ DumpObjects(stream);
161 ✗ fclose(stream); // no need to call close after fclose
162 }
163
164 ✗ return success;
165 }
166
167 ✗ void CommandListReflog::CatalogCallback(
168 const CatalogTraversalData<catalog::Catalog> &data) {
169 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Processing catalog \"%s\"",
170 data.catalog->mountpoint().c_str());
171 const catalog::Catalog::HashVector
172 ✗ &referenced_hashes = data.catalog->GetReferencedObjects();
173 ✗ InsertObjects(referenced_hashes);
174 ✗ if (data.catalog->hash() != objects_->empty_key())
175 ✗ objects_->Insert(data.catalog->hash(), true);
176 }
177
178 ✗ void CommandListReflog::InsertObjects(const vector<shash::Any> &list) {
179 ✗ vector<shash::Any>::const_iterator i = list.begin();
180 ✗ const vector<shash::Any>::const_iterator iend = list.end();
181 ✗ for (; i != iend; ++i) {
182 ✗ if ((*i) != objects_->empty_key())
183 ✗ objects_->Insert(*i, true);
184 }
185 }
186
187 ✗ void CommandListReflog::DumpObjects(FILE *stream) {
188 ✗ const shash::Any empty_key = objects_->empty_key();
189 ✗ shash::Any *hashes = objects_->keys();
190 ✗ for (uint32_t i = 0; i < objects_->capacity(); ++i) {
191 ✗ if (hashes[i] != empty_key) {
192 ✗ fprintf(stream, "%s\n", hashes[i].ToString().c_str());
193 }
194 }
195 }
196
197 } // namespace swissknife
198