GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/receiver/catalog_merge_tool_impl.h
Date: 2026-08-30 02:40:36
Exec Total Coverage
Lines: 172 236 72.9%
Branches: 170 409 41.6%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #ifndef CVMFS_RECEIVER_CATALOG_MERGE_TOOL_IMPL_H_
6 #define CVMFS_RECEIVER_CATALOG_MERGE_TOOL_IMPL_H_
7
8 #include <memory>
9 #include <string>
10 #include <vector>
11
12 #include "catalog.h"
13 #include "catalog_merge_tool.h"
14 #include "catalog_mgr.h"
15 #include "crypto/hash.h"
16 #include "directory_entry.h"
17 #include "manifest.h"
18 #include "options.h"
19 #include "shortstring.h"
20 #include "upload.h"
21 #include "util/exception.h"
22 #include "util/logging.h"
23 #include "util/posix.h"
24 #include "util/raii_temp_dir.h"
25 #include "util/string.h"
26 #include "xattr.h"
27
28 4116 inline PathString MakeRelative(const PathString &path) {
29 4116 std::string rel_path;
30
1/2
✓ Branch 1 taken 4116 times.
✗ Branch 2 not taken.
4116 std::string abs_path = path.ToString();
31
3/4
✓ Branch 1 taken 4116 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3871 times.
✓ Branch 4 taken 245 times.
4116 if (abs_path[0] == '/') {
32
1/2
✓ Branch 1 taken 3871 times.
✗ Branch 2 not taken.
3871 rel_path = abs_path.substr(1);
33 } else {
34
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 rel_path = abs_path;
35 }
36
1/2
✓ Branch 1 taken 4116 times.
✗ Branch 2 not taken.
8232 return PathString(rel_path);
37 4116 }
38
39 245 inline void SplitHardlink(catalog::DirectoryEntry *entry) {
40
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 245 times.
245 if (entry->linkcount() > 1) {
41 LogCvmfs(kLogReceiver, kLogSyslogErr,
42 "CatalogMergeTool - Hardlink found: %s. Hardlinks are not "
43 "supported when publishing through repository gateway and "
44 "will be split.",
45 entry->name().c_str());
46 entry->set_linkcount(1);
47 }
48 245 }
49
50 147 inline void AbortIfHardlinked(const catalog::DirectoryEntry &entry) {
51
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
147 if (entry.linkcount() > 1) {
52 PANIC(kLogSyslogErr,
53 "CatalogMergeTool - Removal of file %s with linkcount > 1 is "
54 "not supported. Aborting",
55 entry.name().c_str());
56 }
57 147 }
58
59 namespace receiver {
60
61 template<typename RwCatalogMgr, typename RoCatalogMgr>
62 245 bool CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::Run(
63 const Params &params, std::string *new_manifest_path,
64 shash::Any *new_manifest_hash, uint64_t *final_rev) {
65 245 std::unique_ptr<upload::Spooler> spooler;
66
2/4
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 245 times.
✗ Branch 6 not taken.
490 perf::StatisticsTemplate stats_tmpl("publish", statistics_);
67
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
490 counters_ = std::unique_ptr<perf::FsCounters>(
68
2/4
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 245 times.
✗ Branch 5 not taken.
245 new perf::FsCounters(stats_tmpl));
69
70 245 std::unique_ptr<RaiiTempDir> raii_temp_dir(
71
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 RaiiTempDir::Create(temp_dir_prefix_));
72
1/2
✓ Branch 0 taken 245 times.
✗ Branch 1 not taken.
245 if (needs_setup_) {
73
2/4
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 245 times.
✗ Branch 7 not taken.
490 upload::SpoolerDefinition definition(
74 245 params.spooler_configuration, params.hash_alg, params.compression_alg,
75 245 params.generate_legacy_bulk_chunks, params.use_file_chunking,
76
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 params.min_chunk_size, params.avg_chunk_size, params.max_chunk_size,
77 "dummy_token", "dummy_key");
78
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 spooler = std::unique_ptr<upload::Spooler>(
79 upload::Spooler::Construct(definition, &stats_tmpl));
80
1/2
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
245 const std::string temp_dir = raii_temp_dir->dir();
81
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
490 output_catalog_mgr_ = std::unique_ptr<RwCatalogMgr>(new RwCatalogMgr(
82 245 manifest_->catalog_hash(), repo_path_, temp_dir, spooler.get(),
83 245 download_manager_, params.enforce_limits, params.nested_kcatalog_limit,
84 245 params.root_kcatalog_limit, params.file_mbyte_limit, statistics_,
85 245 params.use_autocatalogs, params.max_weight, params.min_weight,
86
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 cache_dir_));
87
1/2
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
245 output_catalog_mgr_->Init();
88 245 }
89
90
2/4
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 245 times.
245 if (!CreateMissingAncestorDirs()) {
91 // The catalog manager owns SQLite statements backed by files in
92 // raii_temp_dir and may refer to spooler. Destroy it before those
93 // function-local dependencies go out of scope on this error path.
94 output_catalog_mgr_.reset();
95 return false;
96 }
97
98
3/6
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 245 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 245 times.
✗ Branch 9 not taken.
245 bool ret = CatalogDiffTool<RoCatalogMgr>::Run(PathString(""));
99
100
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 ret &= CreateNewManifest(new_manifest_path);
101 245 *new_manifest_hash = manifest_->catalog_hash();
102 245 *final_rev = manifest_->revision();
103
104 245 output_catalog_mgr_.reset();
105
106 245 return ret;
107 245 }
108
109 template<typename RwCatalogMgr, typename RoCatalogMgr>
110 245 bool CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::CreateMissingAncestorDirs() {
111 // DiffRec does not report ancestors of a scoped lease. Create them top-down
112 // so additions below a previously missing lease path have a parent catalog.
113 // C_N provides names and types only; the receiver supplies their metadata.
114
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 const std::string lease_path = lease_path_.ToString();
115
2/2
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 98 times.
245 if (lease_path.empty()) {
116 147 return true;
117 }
118
119 RoCatalogMgr
120 98 *new_catalog_mgr = CatalogDiffTool<RoCatalogMgr>::GetNewCatalogMgr();
121
122 // An absent lease leaf has no reportable descendants: this is a no-op.
123
2/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
98 const PathString lease_abs_path("/" + lease_path);
124
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 catalog::DirectoryEntry lease_entry;
125
3/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 49 times.
98 if (!new_catalog_mgr->LookupPath(lease_abs_path, catalog::kLookupDefault,
126 &lease_entry)) {
127 49 return true;
128 }
129
130
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 const std::vector<std::string> components = SplitString(lease_path, '/');
131 // Only the proper ancestors of the lease-path leaf are pre-created; the leaf
132 // itself is handled by the DiffRec pass.
133 49 std::string prefix; // relative, no leading slash, e.g. "a/b"
134 49 std::string parent; // parent of prefix, empty at the repository root
135
5/10
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 8 taken 98 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 12 taken 98 times.
✓ Branch 13 taken 49 times.
245 for (size_t i = 0; i + 1 < components.size(); ++i) {
136
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 parent = prefix;
137
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 49 times.
98 if (!prefix.empty()) {
138
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 prefix += "/";
139 }
140
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 prefix += components[i];
141
142
2/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
98 const PathString abs_path("/" + prefix);
143
144
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 catalog::DirectoryEntry existing;
145
2/4
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 98 times.
98 if (output_catalog_mgr_->LookupPath(abs_path, catalog::kLookupDefault,
146 &existing)) {
147 // Already present in the target catalog, e.g. created by a concurrent
148 // sibling lease that committed first. Reuse it; only a non-directory
149 // collision is fatal.
150 if (!existing.IsDirectory()) {
151 LogCvmfs(kLogReceiver, kLogSyslogErr,
152 "CatalogMergeTool - lease path ancestor %s exists but is not "
153 "a directory. Aborting merge.",
154 abs_path.c_str());
155 return false;
156 }
157 continue;
158 }
159
160
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 catalog::DirectoryEntry dirent;
161
2/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 98 times.
98 if (!new_catalog_mgr->LookupPath(abs_path, catalog::kLookupDefault,
162 &dirent)) {
163 LogCvmfs(kLogReceiver, kLogSyslogErr,
164 "CatalogMergeTool - lease path ancestor %s not found in the new "
165 "catalog. Aborting merge.",
166 abs_path.c_str());
167 return false;
168 }
169
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
98 if (!dirent.IsDirectory()) {
170 LogCvmfs(kLogReceiver, kLogSyslogErr,
171 "CatalogMergeTool - lease path ancestor %s is not a directory "
172 "in the new catalog. Aborting merge.",
173 abs_path.c_str());
174 return false;
175 }
176
177 // Ancestors use fixed metadata and no transition flags or xattrs.
178
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 dirent.SetImplicitDirectoryMetadata();
179 98 dirent.set_is_nested_catalog_mountpoint(false);
180 98 dirent.set_is_nested_catalog_root(false);
181 98 dirent.set_is_bind_mountpoint(false);
182 98 dirent.set_is_chunked_file(false);
183 98 dirent.set_is_hidden(false);
184
185
1/2
✓ Branch 3 taken 98 times.
✗ Branch 4 not taken.
98 output_catalog_mgr_->AddDirectory(dirent, XattrList(), parent);
186 98 perf::Inc(counters_->n_directories_added);
187
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(kLogReceiver, kLogSyslog,
188 "CatalogMergeTool - created missing lease path ancestor %s",
189 abs_path.c_str());
190 }
191
192 49 return true;
193 245 }
194
195 template<typename RwCatalogMgr, typename RoCatalogMgr>
196 882 bool CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::IsIgnoredPath(
197 const PathString &path) {
198
1/2
✓ Branch 1 taken 882 times.
✗ Branch 2 not taken.
882 const PathString rel_path = MakeRelative(path);
199
200 // Ignore any paths that are not either within the lease path or
201 // above the lease path
202
1/2
✓ Branch 1 taken 882 times.
✗ Branch 2 not taken.
882 return !(IsSubPath(lease_path_, rel_path)
203
5/6
✓ Branch 0 taken 294 times.
✓ Branch 1 taken 588 times.
✓ Branch 3 taken 294 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 98 times.
✓ Branch 6 taken 196 times.
1764 || IsSubPath(rel_path, lease_path_));
204 882 }
205
206 template<typename RwCatalogMgr, typename RoCatalogMgr>
207 2646 bool CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::IsReportablePath(
208 const PathString &path) {
209
1/2
✓ Branch 1 taken 2646 times.
✗ Branch 2 not taken.
2646 const PathString rel_path = MakeRelative(path);
210
211 // Do not report any changes occurring outside the lease path (which
212 // will be due to other concurrent writers)
213
1/2
✓ Branch 1 taken 2646 times.
✗ Branch 2 not taken.
5292 return IsSubPath(lease_path_, rel_path);
214 2646 }
215
216 template<typename RwCatalogMgr, typename RoCatalogMgr>
217 294 bool CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::ReportAddition(
218 const PathString &path, const catalog::DirectoryEntry &entry,
219 const XattrList &xattrs, const FileChunkList &chunks) {
220
1/2
✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
294 const PathString rel_path = MakeRelative(path);
221
222
3/4
✓ Branch 2 taken 245 times.
✓ Branch 3 taken 49 times.
✓ Branch 5 taken 294 times.
✗ Branch 6 not taken.
539 const std::string parent_path = std::strchr(rel_path.c_str(), '/')
223
3/6
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 245 times.
✓ Branch 5 taken 49 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
784 ? GetParentPath(rel_path).c_str()
224 : "";
225
226
2/2
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 147 times.
294 if (entry.IsDirectory()) {
227
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
147 if (entry.IsNestedCatalogMountpoint()) {
228 // Install the provided nested catalog in the output catalog manager
229 RoCatalogMgr
230 *new_catalog_mgr = CatalogDiffTool<RoCatalogMgr>::GetNewCatalogMgr();
231 PathString mountpoint;
232 shash::Any nested_hash;
233 uint64_t nested_size;
234 const bool found = new_catalog_mgr->LookupNested(
235 path, &mountpoint, &nested_hash, &nested_size);
236 if (!found || !nested_size) {
237 PANIC(kLogSyslogErr,
238 "CatalogMergeTool - nested catalog %s not found. Aborting",
239 rel_path.c_str());
240 }
241 output_catalog_mgr_->GraftNestedCatalog(rel_path.ToString(), nested_hash,
242 nested_size);
243 return false;
244 } else {
245
1/2
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
147 output_catalog_mgr_->AddDirectory(entry, xattrs, parent_path);
246 }
247 147 perf::Inc(counters_->n_directories_added);
248
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 147 times.
✗ Branch 7 not taken.
147 } else if (entry.IsRegular() || entry.IsLink()) {
249
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 catalog::DirectoryEntry modified_entry = entry;
250
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 SplitHardlink(&modified_entry);
251 const catalog::DirectoryEntryBase
252 147 *base_entry = static_cast<const catalog::DirectoryEntryBase *>(
253 &modified_entry);
254
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
147 if (entry.IsChunkedFile()) {
255 assert(!chunks.IsEmpty());
256 output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
257 chunks);
258 } else {
259
1/2
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
147 output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
260 }
261
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
147 if (entry.IsLink()) {
262 perf::Inc(counters_->n_symlinks_added);
263 } else {
264 147 perf::Inc(counters_->n_files_added);
265 }
266
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry.size()));
267 147 }
268 294 return true;
269 294 }
270
271 template<typename RwCatalogMgr, typename RoCatalogMgr>
272 147 void CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::ReportRemoval(
273 const PathString &path, const catalog::DirectoryEntry &entry) {
274
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 const PathString rel_path = MakeRelative(path);
275
276
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 98 times.
147 if (entry.IsDirectory()) {
277
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
49 if (entry.IsNestedCatalogMountpoint()) {
278 output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
279 false);
280 }
281
282
2/4
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
49 output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
283 49 perf::Inc(counters_->n_directories_removed);
284
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
98 } else if (entry.IsRegular() || entry.IsLink()) {
285
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 AbortIfHardlinked(entry);
286
2/4
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 98 times.
✗ Branch 8 not taken.
98 output_catalog_mgr_->RemoveFile(rel_path.c_str());
287
288
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
98 if (entry.IsLink()) {
289 perf::Inc(counters_->n_symlinks_removed);
290 } else {
291 98 perf::Inc(counters_->n_files_removed);
292 }
293
294
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 perf::Xadd(counters_->sz_removed_bytes, static_cast<int64_t>(entry.size()));
295 }
296 147 }
297
298 template<typename RwCatalogMgr, typename RoCatalogMgr>
299 147 bool CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::ReportModification(
300 const PathString &path, const catalog::DirectoryEntry &entry1,
301 const catalog::DirectoryEntry &entry2, const XattrList &xattrs,
302 const FileChunkList &chunks) {
303
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 const PathString rel_path = MakeRelative(path);
304
305
3/4
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 49 times.
✓ Branch 5 taken 147 times.
✗ Branch 6 not taken.
245 const std::string parent_path = std::strchr(rel_path.c_str(), '/')
306
3/6
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✓ Branch 5 taken 49 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
343 ? GetParentPath(rel_path).c_str()
307 : "";
308
309 147 if (entry1.IsNestedCatalogMountpoint()
310
4/6
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 147 times.
147 && entry2.IsNestedCatalogMountpoint()) {
311 // From nested catalog to nested catalog
312 RoCatalogMgr
313 *new_catalog_mgr = CatalogDiffTool<RoCatalogMgr>::GetNewCatalogMgr();
314 PathString mountpoint;
315 shash::Any new_hash;
316 uint64_t new_size;
317 const bool found = new_catalog_mgr->LookupNested(path, &mountpoint,
318 &new_hash, &new_size);
319 if (!found || !new_size) {
320 PANIC(kLogSyslogErr,
321 "CatalogMergeTool - nested catalog %s not found. Aborting",
322 rel_path.c_str());
323 }
324 output_catalog_mgr_->SwapNestedCatalog(rel_path.ToString(), new_hash,
325 new_size);
326 return false; // skip recursion into nested catalog mountpoints
327
6/6
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 49 times.
✓ Branch 5 taken 49 times.
✓ Branch 6 taken 49 times.
✓ Branch 7 taken 49 times.
✓ Branch 8 taken 98 times.
147 } else if (entry1.IsDirectory() && entry2.IsDirectory()) {
328 // From directory to directory
329 const catalog::DirectoryEntryBase
330 49 *base_entry = static_cast<const catalog::DirectoryEntryBase *>(&entry2);
331
2/4
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
49 output_catalog_mgr_->TouchDirectory(*base_entry, xattrs, rel_path.c_str());
332 49 if (!entry1.IsNestedCatalogMountpoint()
333
3/6
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 49 times.
49 && entry2.IsNestedCatalogMountpoint()) {
334 output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
335 49 } else if (entry1.IsNestedCatalogMountpoint()
336
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 49 times.
49 && !entry2.IsNestedCatalogMountpoint()) {
337 output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()));
338 }
339 49 perf::Inc(counters_->n_directories_changed);
340
5/8
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 49 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 49 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 49 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 98 times.
98 } else if ((entry1.IsRegular() || entry1.IsLink()) && entry2.IsDirectory()) {
341 // From file to directory
342 AbortIfHardlinked(entry1);
343 output_catalog_mgr_->RemoveFile(rel_path.c_str());
344 output_catalog_mgr_->AddDirectory(entry2, xattrs, parent_path);
345 if (entry2.IsNestedCatalogMountpoint()) {
346 output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
347 }
348 if (entry1.IsLink()) {
349 perf::Inc(counters_->n_symlinks_removed);
350 } else {
351 perf::Inc(counters_->n_files_removed);
352 }
353 perf::Xadd(counters_->sz_removed_bytes,
354 static_cast<int64_t>(entry1.size()));
355 perf::Inc(counters_->n_directories_added);
356
357
6/8
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 49 times.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 49 times.
✓ Branch 10 taken 49 times.
98 } else if (entry1.IsDirectory() && (entry2.IsRegular() || entry2.IsLink())) {
358 // From directory to file
359
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 if (entry1.IsNestedCatalogMountpoint()) {
360 // we merge the nested catalog with its parent, it will be the recursive
361 // procedure that will take care of deleting all the files.
362
2/4
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
49 output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
363 /* merge = */ true);
364 }
365
366
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 catalog::DirectoryEntry modified_entry = entry2;
367
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 SplitHardlink(&modified_entry);
368 const catalog::DirectoryEntryBase
369 49 *base_entry = static_cast<const catalog::DirectoryEntryBase *>(
370 &modified_entry);
371
372
2/4
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
49 output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
373
374
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
49 if (entry2.IsChunkedFile()) {
375 assert(!chunks.IsEmpty());
376 output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
377 chunks);
378 } else {
379
1/2
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
49 output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
380 }
381
382 49 perf::Inc(counters_->n_directories_removed);
383
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 if (entry2.IsLink()) {
384 49 perf::Inc(counters_->n_symlinks_added);
385 } else {
386 perf::Inc(counters_->n_files_added);
387 }
388
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
389
390
0/2
✗ Branch 3 not taken.
✗ Branch 4 not taken.
98 } else if ((entry1.IsRegular() || entry1.IsLink())
391
3/8
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 49 times.
✗ Branch 9 not taken.
49 && (entry2.IsRegular() || entry2.IsLink())) {
392 // From file to file
393
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 AbortIfHardlinked(entry1);
394
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 catalog::DirectoryEntry modified_entry = entry2;
395
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 SplitHardlink(&modified_entry);
396 const catalog::DirectoryEntryBase
397 49 *base_entry = static_cast<const catalog::DirectoryEntryBase *>(
398 &modified_entry);
399
2/4
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✗ Branch 8 not taken.
49 output_catalog_mgr_->RemoveFile(rel_path.c_str());
400
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
49 if (entry2.IsChunkedFile()) {
401 assert(!chunks.IsEmpty());
402 output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
403 chunks);
404 } else {
405
1/2
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
49 output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
406 }
407
408
3/6
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 49 times.
✗ Branch 7 not taken.
49 if (entry1.IsRegular() && entry2.IsRegular()) {
409 49 perf::Inc(counters_->n_files_changed);
410 } else if (entry1.IsRegular() && entry2.IsLink()) {
411 perf::Inc(counters_->n_files_removed);
412 perf::Inc(counters_->n_symlinks_added);
413 } else if (entry1.IsLink() && entry2.IsRegular()) {
414 perf::Inc(counters_->n_symlinks_removed);
415 perf::Inc(counters_->n_files_added);
416 } else {
417 perf::Inc(counters_->n_symlinks_changed);
418 }
419 49 perf::Xadd(counters_->sz_removed_bytes,
420
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 static_cast<int64_t>(entry1.size()));
421
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
422 49 }
423 147 return true;
424 147 }
425
426 template<typename RwCatalogMgr, typename RoCatalogMgr>
427 245 bool CatalogMergeTool<RwCatalogMgr, RoCatalogMgr>::CreateNewManifest(
428 std::string *new_manifest_path) {
429
2/4
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 245 times.
245 if (!output_catalog_mgr_->Commit(false, 0, manifest_)) {
430 LogCvmfs(kLogReceiver, kLogSyslogErr,
431 "CatalogMergeTool - Could not commit output catalog");
432 return false;
433 }
434
435
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 const std::string new_path = CreateTempPath(temp_dir_prefix_, 0600);
436
437
2/4
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 245 times.
245 if (!manifest_->Export(new_path)) {
438 LogCvmfs(kLogReceiver, kLogSyslogErr,
439 "CatalogMergeTool - Could not export new manifest");
440 }
441
442
1/2
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
245 *new_manifest_path = new_path;
443
444 245 return true;
445 245 }
446
447 } // namespace receiver
448
449 #endif // CVMFS_RECEIVER_CATALOG_MERGE_TOOL_IMPL_H_
450