GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/manifest.cc
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 136 156 87.2%
Branches: 154 340 45.3%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "manifest.h"
6
7 #include <cstdio>
8 #include <map>
9 #include <vector>
10
11 #include "catalog.h"
12 #include "util/posix.h"
13 #include "util/string.h"
14
15 using namespace std; // NOLINT
16
17 namespace manifest {
18
19 393 Breadcrumb::Breadcrumb(const std::string &from_string) {
20 393 timestamp = 0;
21 393 revision = 0; // for backward compatibility: no revision --> revision = 0
22
23
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 378 times.
393 if (from_string.empty()) {
24 15 return;
25 }
26
27 // Separate hash from timestamp
28
1/2
✓ Branch 1 taken 378 times.
✗ Branch 2 not taken.
378 std::vector<std::string> vec_split_timestamp = SplitString(from_string, 'T');
29
30
1/2
✓ Branch 3 taken 378 times.
✗ Branch 4 not taken.
378 catalog_hash = shash::MkFromHexPtr(shash::HexPtr(vec_split_timestamp[0]),
31 shash::kSuffixCatalog);
32
33
2/2
✓ Branch 1 taken 348 times.
✓ Branch 2 taken 30 times.
378 if (vec_split_timestamp.size() > 1) {
34 // check if revision number is included
35 std::vector<std::string> vec_split_revision = SplitString(
36
1/2
✓ Branch 2 taken 348 times.
✗ Branch 3 not taken.
348 vec_split_timestamp[1], 'R');
37
38 // Get local last modified time
39
1/2
✓ Branch 2 taken 348 times.
✗ Branch 3 not taken.
348 timestamp = String2Uint64(vec_split_revision[0]);
40
41 // Get local revision
42
2/2
✓ Branch 1 taken 213 times.
✓ Branch 2 taken 135 times.
348 if (vec_split_revision.size() > 1) {
43
1/2
✓ Branch 2 taken 213 times.
✗ Branch 3 not taken.
213 revision = String2Uint64(vec_split_revision[1]);
44 }
45 348 }
46 378 }
47
48 392 bool Breadcrumb::Export(const string &fqrn, const string &directory,
49 const int mode) const {
50 const string breadcrumb_path =
51
3/6
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 392 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 392 times.
✗ Branch 8 not taken.
784 MakeCanonicalPath(directory) + "/cvmfschecksum." + fqrn;
52 392 string tmp_path;
53
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 FILE *fbreadcrumb = CreateTempFile(breadcrumb_path, mode, "w", &tmp_path);
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if (fbreadcrumb == NULL)
55 return false;
56
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 string str_breadcrumb = ToString();
57 const int written =
58
2/4
✓ Branch 2 taken 392 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 392 times.
✗ Branch 6 not taken.
392 fwrite(&(str_breadcrumb[0]), 1, str_breadcrumb.length(), fbreadcrumb);
59
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 fclose(fbreadcrumb);
60
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 392 times.
392 if (static_cast<unsigned>(written) != str_breadcrumb.length()) {
61 unlink(tmp_path.c_str());
62 return false;
63 }
64 392 const int retval = rename(tmp_path.c_str(), breadcrumb_path.c_str());
65
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if (retval != 0) {
66 unlink(tmp_path.c_str());
67 return false;
68 }
69 392 return true;
70 392 }
71
72 976 std::string Breadcrumb::ToString() const {
73
2/4
✓ Branch 1 taken 976 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 976 times.
✗ Branch 5 not taken.
1952 return catalog_hash.ToString() + "T"
74
3/6
✓ Branch 1 taken 976 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 976 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 976 times.
✗ Branch 8 not taken.
3904 + StringifyInt(static_cast<int64_t>(timestamp)) + "R"
75
1/2
✓ Branch 2 taken 976 times.
✗ Branch 3 not taken.
2928 + StringifyUint(revision);
76 }
77
78
79 //------------------------------------------------------------------------------
80
81
82 1128 Manifest *Manifest::LoadMem(const unsigned char *buffer,
83 const unsigned length) {
84 1128 map<char, string> content;
85
1/2
✓ Branch 1 taken 1128 times.
✗ Branch 2 not taken.
1128 ParseKeyvalMem(buffer, length, &content);
86
87
1/2
✓ Branch 1 taken 1128 times.
✗ Branch 2 not taken.
2256 return Load(content);
88 1128 }
89
90
91 196 Manifest *Manifest::LoadFile(const std::string &from_path) {
92 196 map<char, string> content;
93
2/4
✓ Branch 1 taken 196 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 196 times.
196 if (!ParseKeyvalPath(from_path, &content))
94 return NULL;
95
96
1/2
✓ Branch 1 taken 196 times.
✗ Branch 2 not taken.
196 return Load(content);
97 196 }
98
99
100 1324 Manifest *Manifest::Load(const map<char, string> &content) {
101 1324 map<char, string>::const_iterator iter;
102
103 // Required keys
104
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 shash::Any catalog_hash;
105
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 shash::Md5 root_path;
106 uint32_t ttl;
107 uint64_t revision;
108
109
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 iter = content.find('C');
110
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1324 times.
1324 if ((iter = content.find('C')) == content.end())
111 return NULL;
112
1/2
✓ Branch 3 taken 1324 times.
✗ Branch 4 not taken.
1324 catalog_hash = MkFromHexPtr(shash::HexPtr(iter->second),
113 shash::kSuffixCatalog);
114
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1324 times.
1324 if ((iter = content.find('R')) == content.end())
115 return NULL;
116
1/2
✓ Branch 3 taken 1324 times.
✗ Branch 4 not taken.
1324 root_path = shash::Md5(shash::HexPtr(iter->second));
117
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1324 times.
1324 if ((iter = content.find('D')) == content.end())
118 return NULL;
119
1/2
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
1324 ttl = String2Uint64(iter->second);
120
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1324 times.
1324 if ((iter = content.find('S')) == content.end())
121 return NULL;
122
1/2
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
1324 revision = String2Uint64(iter->second);
123
124
125 // Optional keys
126 1324 uint64_t catalog_size = 0;
127
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 shash::Any micro_catalog_hash;
128 1324 string repository_name;
129
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 shash::Any certificate;
130
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 shash::Any history;
131 1324 uint64_t publish_timestamp = 0;
132 1324 bool garbage_collectable = false;
133 1324 bool has_alt_catalog_path = false;
134
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 shash::Any meta_info;
135
1/2
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
1324 shash::Any reflog_hash;
136
137
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1324 times.
✗ Branch 6 not taken.
1324 if ((iter = content.find('B')) != content.end())
138
1/2
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
1324 catalog_size = String2Uint64(iter->second);
139
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1324 times.
1324 if ((iter = content.find('L')) != content.end())
140 micro_catalog_hash = MkFromHexPtr(shash::HexPtr(iter->second),
141 shash::kSuffixMicroCatalog);
142
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1324 times.
✗ Branch 6 not taken.
1324 if ((iter = content.find('N')) != content.end())
143
1/2
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
1324 repository_name = iter->second;
144
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1324 times.
✗ Branch 6 not taken.
1324 if ((iter = content.find('X')) != content.end())
145
1/2
✓ Branch 3 taken 1324 times.
✗ Branch 4 not taken.
1324 certificate = MkFromHexPtr(shash::HexPtr(iter->second),
146 shash::kSuffixCertificate);
147
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1324 times.
✗ Branch 6 not taken.
1324 if ((iter = content.find('H')) != content.end())
148
1/2
✓ Branch 3 taken 1324 times.
✗ Branch 4 not taken.
1324 history = MkFromHexPtr(shash::HexPtr(iter->second), shash::kSuffixHistory);
149
3/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 883 times.
✓ Branch 6 taken 441 times.
1324 if ((iter = content.find('T')) != content.end())
150
1/2
✓ Branch 2 taken 883 times.
✗ Branch 3 not taken.
883 publish_timestamp = String2Uint64(iter->second);
151
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1324 times.
✗ Branch 6 not taken.
1324 if ((iter = content.find('G')) != content.end())
152 1324 garbage_collectable = (iter->second == "yes");
153
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1324 times.
✗ Branch 6 not taken.
1324 if ((iter = content.find('A')) != content.end())
154 1324 has_alt_catalog_path = (iter->second == "yes");
155
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1324 times.
1324 if ((iter = content.find('M')) != content.end())
156 meta_info = MkFromHexPtr(shash::HexPtr(iter->second),
157 shash::kSuffixMetainfo);
158
2/5
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1324 times.
1324 if ((iter = content.find('Y')) != content.end()) {
159 reflog_hash = MkFromHexPtr(shash::HexPtr(iter->second));
160 }
161
162 return new Manifest(catalog_hash, catalog_size, root_path, ttl, revision,
163 micro_catalog_hash, repository_name, certificate, history,
164 publish_timestamp, garbage_collectable,
165
2/4
✓ Branch 1 taken 1324 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1324 times.
✗ Branch 5 not taken.
1324 has_alt_catalog_path, meta_info, reflog_hash);
166 1324 }
167
168
169 5377 Manifest::Manifest(const shash::Any &catalog_hash,
170 const uint64_t catalog_size,
171 5377 const string &root_path)
172 5377 : catalog_hash_(catalog_hash)
173 5377 , catalog_size_(catalog_size)
174
1/2
✓ Branch 2 taken 5377 times.
✗ Branch 3 not taken.
5377 , root_path_(shash::Md5(shash::AsciiPtr(root_path)))
175 5377 , ttl_(catalog::Catalog::kDefaultTTL)
176 5377 , revision_(0)
177 5377 , publish_timestamp_(0)
178 5377 , garbage_collectable_(false)
179
4/8
✓ Branch 3 taken 5377 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5377 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 5377 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 5377 times.
✗ Branch 13 not taken.
5377 , has_alt_catalog_path_(false) { }
180
181
182 /**
183 * Creates the manifest string
184 */
185 1881 string Manifest::ExportString() const {
186
4/8
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1881 times.
✗ Branch 11 not taken.
3762 string manifest = "C" + catalog_hash_.ToString() + "\n" + "B"
187
4/8
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1881 times.
✗ Branch 11 not taken.
7524 + StringifyInt(catalog_size_) + "\n" + "R"
188
6/12
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1881 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1881 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1881 times.
✗ Branch 17 not taken.
7524 + root_path_.ToString() + "\n" + "D" + StringifyInt(ttl_)
189
6/12
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1881 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1881 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1881 times.
✗ Branch 17 not taken.
7524 + "\n" + "S" + StringifyInt(revision_) + "\n" + "G"
190
4/8
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1881 times.
✗ Branch 11 not taken.
7524 + StringifyBool(garbage_collectable_) + "\n" + "A"
191
2/4
✓ Branch 2 taken 1881 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1881 times.
✗ Branch 6 not taken.
5643 + StringifyBool(has_alt_catalog_path_) + "\n";
192
193
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1881 times.
1881 if (!micro_catalog_hash_.IsNull())
194 manifest += "L" + micro_catalog_hash_.ToString() + "\n";
195
1/2
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
1881 if (repository_name_ != "")
196
3/6
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
1881 manifest += "N" + repository_name_ + "\n";
197
1/2
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
1881 if (!certificate_.IsNull())
198
4/8
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1881 times.
✗ Branch 11 not taken.
1881 manifest += "X" + certificate_.ToString() + "\n";
199
1/2
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
1881 if (!history_.IsNull())
200
4/8
✓ Branch 1 taken 1881 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1881 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1881 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1881 times.
✗ Branch 11 not taken.
1881 manifest += "H" + history_.ToString() + "\n";
201
2/2
✓ Branch 0 taken 1251 times.
✓ Branch 1 taken 630 times.
1881 if (publish_timestamp_ > 0)
202
4/8
✓ Branch 1 taken 1251 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1251 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1251 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1251 times.
✗ Branch 11 not taken.
1251 manifest += "T" + StringifyInt(publish_timestamp_) + "\n";
203
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1881 times.
1881 if (!meta_info_.IsNull())
204 manifest += "M" + meta_info_.ToString() + "\n";
205
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1881 times.
1881 if (!reflog_hash_.IsNull()) {
206 manifest += "Y" + reflog_hash_.ToString() + "\n";
207 }
208 // Reserved: Z -> for identification of channel tips
209
210 1881 return manifest;
211 }
212
213
214 /**
215 * Writes the .cvmfspublished file (unsigned).
216 */
217 98 bool Manifest::Export(const std::string &path) const {
218
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 FILE *fmanifest = fopen(path.c_str(), "w");
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 if (!fmanifest)
220 return false;
221
222
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 string manifest = ExportString();
223
224
1/2
✓ Branch 3 taken 98 times.
✗ Branch 4 not taken.
98 if (fwrite(manifest.data(), 1, manifest.length(), fmanifest)
225
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
98 != manifest.length()) {
226 fclose(fmanifest);
227 unlink(path.c_str());
228 return false;
229 }
230
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 fclose(fmanifest);
231
232 98 return true;
233 98 }
234
235
236 /**
237 * Writes the cvmfschecksum.$repository file. Atomic store.
238 */
239 392 bool Manifest::ExportBreadcrumb(const string &directory, const int mode) const {
240 784 return Breadcrumb(catalog_hash_, publish_timestamp_, revision_)
241
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
784 .Export(repository_name_, directory, mode);
242 }
243
244
245 /**
246 * Read the hash and the last-modified time stamp from the
247 * cvmfschecksum.$repository file in the given directory.
248 */
249 1004 Breadcrumb Manifest::ReadBreadcrumb(const std::string &repo_name,
250 const std::string &directory) {
251
1/2
✓ Branch 1 taken 1004 times.
✗ Branch 2 not taken.
1004 Breadcrumb breadcrumb;
252
2/4
✓ Branch 1 taken 1004 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1004 times.
✗ Branch 5 not taken.
1004 const string breadcrumb_path = directory + "/cvmfschecksum." + repo_name;
253
1/2
✓ Branch 2 taken 1004 times.
✗ Branch 3 not taken.
1004 FILE *fbreadcrumb = fopen(breadcrumb_path.c_str(), "r");
254
2/2
✓ Branch 0 taken 746 times.
✓ Branch 1 taken 258 times.
1004 if (!fbreadcrumb) {
255 // Return invalid breadcrumb if not found
256 746 return breadcrumb;
257 }
258 char tmp[164];
259
1/2
✓ Branch 1 taken 258 times.
✗ Branch 2 not taken.
258 const size_t read_bytes = fread(tmp, 1, 164, fbreadcrumb);
260
2/2
✓ Branch 0 taken 243 times.
✓ Branch 1 taken 15 times.
258 if (read_bytes > 0) {
261
2/4
✓ Branch 2 taken 243 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 243 times.
✗ Branch 6 not taken.
243 breadcrumb = Breadcrumb(std::string(tmp, read_bytes));
262 }
263
1/2
✓ Branch 1 taken 258 times.
✗ Branch 2 not taken.
258 fclose(fbreadcrumb);
264
265 258 return breadcrumb;
266 1004 }
267
268 } // namespace manifest
269