GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_rw.cc
Date: 2026-10-04 02:40:33
Exec Total Coverage
Lines: 315 401 78.6%
Branches: 274 743 36.9%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "catalog_rw.h"
6
7 #include <inttypes.h>
8
9 #include <cstdio>
10 #include <cstdlib>
11
12 #include "util/exception.h"
13 #include "util/logging.h"
14 #include "xattr.h"
15
16 using namespace std; // NOLINT
17
18 namespace catalog {
19
20 const double WritableCatalog::kMaximalFreePageRatio = 0.20;
21 const double WritableCatalog::kMaximalRowIdWasteRatio = 0.25;
22
23
24 1813 WritableCatalog::WritableCatalog(const string &path,
25 const shash::Any &catalog_hash,
26 Catalog *parent,
27 1813 const bool is_not_root)
28 3626 : Catalog(PathString(path.data(), path.length()),
29 catalog_hash, // This is 0 for a newly created catalog!
30 parent,
31 is_not_root)
32 1813 , sql_insert_(NULL)
33 1813 , sql_unlink_(NULL)
34 1813 , sql_touch_(NULL)
35 1813 , sql_update_(NULL)
36 1813 , sql_chunk_insert_(NULL)
37 1813 , sql_chunks_remove_(NULL)
38 1813 , sql_chunks_count_(NULL)
39 1813 , sql_max_link_id_(NULL)
40 1813 , sql_inc_linkcount_(NULL)
41
2/4
✓ Branch 2 taken 1813 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1813 times.
✗ Branch 6 not taken.
5439 , dirty_(false) {
42 1813 atomic_init32(&dirty_children_);
43 1813 }
44
45
46 140 WritableCatalog *WritableCatalog::AttachFreely(const string &root_path,
47 const string &file,
48 const shash::Any &catalog_hash,
49 Catalog *parent,
50 const bool is_not_root) {
51 WritableCatalog *catalog = new WritableCatalog(root_path, catalog_hash,
52
1/2
✓ Branch 2 taken 140 times.
✗ Branch 3 not taken.
140 parent, is_not_root);
53 140 const bool successful_init = catalog->InitStandalone(file);
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 140 times.
140 if (!successful_init) {
55 ✗ delete catalog;
56 ✗ return NULL;
57 }
58 140 return catalog;
59 }
60
61
62 7216 WritableCatalog::~WritableCatalog() {
63 // CAUTION HOT!
64 // (see Catalog.h - near the definition of FinalizePreparedStatements)
65 3608 FinalizePreparedStatements();
66 7216 }
67
68
69 1510 void WritableCatalog::Transaction() {
70
1/3
✓ Branch 2 taken 1510 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1510 LogCvmfs(kLogCatalog, kLogVerboseMsg, "opening SQLite transaction for '%s'",
71 3020 mountpoint().c_str());
72 1510 const bool retval = database().BeginTransaction();
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1510 times.
1510 assert(retval == true);
74 1510 }
75
76
77 1369 void WritableCatalog::Commit() {
78
1/3
✓ Branch 2 taken 1369 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1369 LogCvmfs(kLogCatalog, kLogVerboseMsg, "closing SQLite transaction for '%s'",
79 2738 mountpoint().c_str());
80 1369 const bool retval = database().CommitTransaction();
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1369 times.
1369 assert(retval == true);
82 1369 dirty_ = false;
83 1369 }
84
85
86 1813 void WritableCatalog::InitPreparedStatements() {
87 1813 Catalog::InitPreparedStatements(); // polymorphism: up call
88
89
2/4
✓ Branch 2 taken 1813 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1813 times.
✗ Branch 7 not taken.
3626 const bool retval = SqlCatalog(database(), "PRAGMA foreign_keys = ON;")
90
1/2
✓ Branch 1 taken 1813 times.
✗ Branch 2 not taken.
1813 .Execute();
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1813 times.
1813 assert(retval);
92
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_insert_ = new SqlDirentInsert(database());
93
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_unlink_ = new SqlDirentUnlink(database());
94
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_touch_ = new SqlDirentTouch(database());
95
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_update_ = new SqlDirentUpdate(database());
96
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_chunk_insert_ = new SqlChunkInsert(database());
97
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_chunks_remove_ = new SqlChunksRemove(database());
98
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_chunks_count_ = new SqlChunksCount(database());
99
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_max_link_id_ = new SqlMaxHardlinkGroup(database());
100
1/2
✓ Branch 3 taken 1813 times.
✗ Branch 4 not taken.
1813 sql_inc_linkcount_ = new SqlIncLinkcount(database());
101 1813 }
102
103
104 1804 void WritableCatalog::FinalizePreparedStatements() {
105 // no polymorphism: no up call (see Catalog.h -
106 // near the definition of this method)
107
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_insert_;
108
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_unlink_;
109
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_touch_;
110
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_update_;
111
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_chunk_insert_;
112
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_chunks_remove_;
113
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_chunks_count_;
114
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_max_link_id_;
115
1/2
✓ Branch 0 taken 1804 times.
✗ Branch 1 not taken.
1804 delete sql_inc_linkcount_;
116 1804 }
117
118
119 /**
120 * Find out the maximal hardlink group id in this catalog.
121 */
122 38 uint32_t WritableCatalog::GetMaxLinkId() const {
123 38 int result = -1;
124
125
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 if (sql_max_link_id_->FetchRow()) {
126 38 result = sql_max_link_id_->GetMaxGroupId();
127 }
128 38 sql_max_link_id_->Reset();
129
130 38 return result;
131 }
132
133
134 /**
135 * Adds a directory entry.
136 * @param entry the DirectoryEntry to add to the catalog
137 * @param entry_path the full path of the DirectoryEntry to add
138 * @param parent_path the full path of the containing directory
139 */
140 10557 void WritableCatalog::AddEntry(const DirectoryEntry &entry,
141 const XattrList &xattrs,
142 const string &entry_path,
143 const string &parent_path) {
144
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 SetDirty();
145
146
1/6
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10557 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
10557 LogCvmfs(kLogCatalog, kLogVerboseMsg, "add entry '%s' to '%s'",
147
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
21114 entry_path.c_str(), mountpoint().c_str());
148
149
1/2
✓ Branch 2 taken 10557 times.
✗ Branch 3 not taken.
10557 const shash::Md5 path_hash((shash::AsciiPtr(entry_path)));
150
1/2
✓ Branch 2 taken 10557 times.
✗ Branch 3 not taken.
10557 const shash::Md5 parent_hash((shash::AsciiPtr(parent_path)));
151
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 DirectoryEntry effective_entry(entry);
152 10557 effective_entry.set_has_xattrs(!xattrs.IsEmpty());
153
154
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 bool retval = sql_insert_->BindPathHash(path_hash)
155
2/4
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10557 times.
✗ Branch 4 not taken.
10557 && sql_insert_->BindParentPathHash(parent_hash)
156
3/6
✓ Branch 0 taken 10557 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 10557 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10557 times.
✗ Branch 6 not taken.
21114 && sql_insert_->BindDirent(effective_entry);
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10557 times.
10557 assert(retval);
158
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 if (xattrs.IsEmpty()) {
159
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 retval = sql_insert_->BindXattrEmpty();
160 } else {
161 ✗ retval = sql_insert_->BindXattr(xattrs);
162 }
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10557 times.
10557 assert(retval);
164
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 retval = sql_insert_->Execute();
165
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10557 times.
10557 if (!retval) {
166 ✗ PANIC(kLogStderr, "failed to add '%s' (parent '%s') to catalog '%s': %s",
167 entry_path.c_str(), parent_path.c_str(), mountpoint().c_str(),
168 database().GetLastErrorMsg().c_str());
169 }
170
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 sql_insert_->Reset();
171
172
1/2
✓ Branch 1 taken 10557 times.
✗ Branch 2 not taken.
10557 delta_counters_.Increment(effective_entry);
173 10557 }
174
175
176 /**
177 * Removes the specified entry from the catalog.
178 * Note: removing a directory which is non-empty results in dangling entries.
179 * (this should be treated in upper layers)
180 * @param entry_path the full path of the DirectoryEntry to delete
181 */
182 3041 void WritableCatalog::RemoveEntry(const string &file_path) {
183
1/2
✓ Branch 1 taken 3041 times.
✗ Branch 2 not taken.
3041 DirectoryEntry entry;
184
2/4
✓ Branch 1 taken 3041 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3041 times.
✗ Branch 5 not taken.
3041 bool retval = LookupPath(PathString(file_path), &entry);
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3041 times.
3041 assert(retval);
186
187
1/2
✓ Branch 1 taken 3041 times.
✗ Branch 2 not taken.
3041 SetDirty();
188
189 // If the entry used to be a chunked file... remove the chunks
190
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3041 times.
3041 if (entry.IsChunkedFile()) {
191 ✗ RemoveFileChunks(file_path);
192 }
193
194 // remove the entry itself
195
1/2
✓ Branch 2 taken 3041 times.
✗ Branch 3 not taken.
3041 const shash::Md5 path_hash = shash::Md5(shash::AsciiPtr(file_path));
196
4/9
✓ Branch 1 taken 3041 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3041 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3041 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3041 times.
✗ Branch 9 not taken.
3041 retval = sql_unlink_->BindPathHash(path_hash) && sql_unlink_->Execute();
197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3041 times.
3041 assert(retval);
198
1/2
✓ Branch 1 taken 3041 times.
✗ Branch 2 not taken.
3041 sql_unlink_->Reset();
199
200
1/2
✓ Branch 1 taken 3041 times.
✗ Branch 2 not taken.
3041 delta_counters_.Decrement(entry);
201 3041 }
202
203
204 ✗ void WritableCatalog::IncLinkcount(const string &path_within_group,
205 const int delta) {
206 ✗ SetDirty();
207
208 ✗ const shash::Md5 path_hash = shash::Md5(shash::AsciiPtr(path_within_group));
209
210 ✗ const bool retval = sql_inc_linkcount_->BindPathHash(path_hash)
211 ✗ && sql_inc_linkcount_->BindDelta(delta)
212 ✗ && sql_inc_linkcount_->Execute();
213 ✗ assert(retval);
214 ✗ sql_inc_linkcount_->Reset();
215 }
216
217
218 42 void WritableCatalog::TouchEntry(const DirectoryEntryBase &entry,
219 const XattrList &xattrs,
220 const shash::Md5 &path_hash) {
221
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 SetDirty();
222
223
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 catalog::DirectoryEntry prev_entry;
224
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 bool retval = LookupMd5Path(path_hash, &prev_entry);
225
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 assert(retval);
226
227
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 retval = sql_touch_->BindPathHash(path_hash)
228
3/6
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 42 times.
✗ Branch 6 not taken.
42 && sql_touch_->BindDirentBase(entry);
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 assert(retval);
230
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 if (xattrs.IsEmpty()) {
231
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 retval = sql_touch_->BindXattrEmpty();
232
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
42 if (prev_entry.HasXattrs())
233 ✗ delta_counters_.self.xattrs--;
234 } else {
235 ✗ retval = sql_touch_->BindXattr(xattrs);
236 ✗ if (!prev_entry.HasXattrs())
237 ✗ delta_counters_.self.xattrs++;
238 }
239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 assert(retval);
240
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 retval = sql_touch_->Execute();
241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 assert(retval);
242
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 sql_touch_->Reset();
243 42 }
244
245
246 4728 void WritableCatalog::UpdateEntry(const DirectoryEntry &entry,
247 const shash::Md5 &path_hash) {
248 4728 SetDirty();
249
250 4728 const bool retval = sql_update_->BindPathHash(path_hash)
251
1/2
✓ Branch 1 taken 4728 times.
✗ Branch 2 not taken.
4728 && sql_update_->BindDirent(entry)
252
2/4
✓ Branch 0 taken 4728 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4728 times.
✗ Branch 4 not taken.
9456 && sql_update_->Execute();
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4728 times.
4728 assert(retval);
254 4728 sql_update_->Reset();
255 4728 }
256
257 64 void WritableCatalog::AddFileChunk(const std::string &entry_path,
258 const FileChunk &chunk) {
259
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 SetDirty();
260
261
1/2
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
64 const shash::Md5 path_hash((shash::AsciiPtr(entry_path)));
262
263
1/4
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
64 LogCvmfs(kLogCatalog, kLogVerboseMsg,
264 "adding chunk for %s from offset %ld "
265 "and chunk size: %ld bytes",
266 64 entry_path.c_str(), chunk.offset(), chunk.offset() + chunk.size());
267
268 64 delta_counters_.self.file_chunks++;
269
270
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 const bool retval = sql_chunk_insert_->BindPathHash(path_hash)
271
2/4
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
64 && sql_chunk_insert_->BindFileChunk(chunk)
272
3/6
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 64 times.
✗ Branch 6 not taken.
128 && sql_chunk_insert_->Execute();
273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(retval);
274
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 sql_chunk_insert_->Reset();
275 64 }
276
277
278 /**
279 * Removes the file chunks for a given file path
280 * @param entry_path the file path to clear from it's file chunks
281 */
282 ✗ void WritableCatalog::RemoveFileChunks(const std::string &entry_path) {
283 ✗ const shash::Md5 path_hash((shash::AsciiPtr(entry_path)));
284 bool retval;
285
286 // subtract the number of chunks from the statistics counters
287 ✗ retval = sql_chunks_count_->BindPathHash(path_hash)
288 ✗ && sql_chunks_count_->Execute();
289 ✗ assert(retval);
290 ✗ const int chunks_count = sql_chunks_count_->GetChunkCount();
291 ✗ delta_counters_.self.file_chunks -= chunks_count;
292 ✗ sql_chunks_count_->Reset();
293
294 // remove the chunks associated to `entry_path`
295 ✗ retval = sql_chunks_remove_->BindPathHash(path_hash)
296 ✗ && sql_chunks_remove_->Execute();
297 ✗ assert(retval);
298 ✗ sql_chunks_remove_->Reset();
299 }
300
301
302 /**
303 * Sets the last modified time stamp of this catalog to current time.
304 */
305 1795 void WritableCatalog::UpdateLastModified() {
306
2/4
✓ Branch 4 taken 1795 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1795 times.
✗ Branch 8 not taken.
1795 database().SetProperty("last_modified", static_cast<uint64_t>(time(NULL)));
307 1795 }
308
309
310 /**
311 * Increments the revision of the catalog in the database.
312 */
313 1191 void WritableCatalog::IncrementRevision() { SetRevision(GetRevision() + 1); }
314
315
316 1191 void WritableCatalog::SetRevision(const uint64_t new_revision) {
317
2/4
✓ Branch 3 taken 1191 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1191 times.
✗ Branch 7 not taken.
1191 database().SetProperty("revision", new_revision);
318 1191 }
319
320
321 ✗ void WritableCatalog::SetBranch(const std::string &branch_name) {
322 ✗ database().SetProperty("branch", branch_name);
323 }
324
325
326 ✗ void WritableCatalog::SetTTL(const uint64_t new_ttl) {
327 ✗ database().SetProperty("TTL", new_ttl);
328 }
329
330
331 ✗ bool WritableCatalog::SetVOMSAuthz(const std::string &voms_authz) {
332 ✗ return database().SetVOMSAuthz(voms_authz);
333 }
334
335
336 /**
337 * Sets the content hash of the previous catalog revision.
338 */
339 1191 void WritableCatalog::SetPreviousRevision(const shash::Any &hash) {
340
2/4
✓ Branch 4 taken 1191 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1191 times.
✗ Branch 8 not taken.
1191 database().SetProperty("previous_revision", hash.ToString());
341 1191 }
342
343
344 /**
345 * Moves a subtree from this catalog into a just created nested catalog.
346 */
347 690 void WritableCatalog::Partition(WritableCatalog *new_nested_catalog) {
348 // Create connection between parent and child catalogs
349
3/6
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 690 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 690 times.
✗ Branch 8 not taken.
690 MakeTransitionPoint(new_nested_catalog->mountpoint().ToString());
350
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
690 new_nested_catalog->MakeNestedRoot();
351 690 delta_counters_.subtree.directories++; // Root directory in nested catalog
352
353 // Move the present directory tree into the newly created nested catalog
354 // if we hit nested catalog mountpoints on the way, we return them through
355 // the passed list
356 690 vector<string> GrandChildMountpoints;
357
3/6
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 690 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 690 times.
✗ Branch 8 not taken.
690 MoveToNested(new_nested_catalog->mountpoint().ToString(), new_nested_catalog,
358 &GrandChildMountpoints);
359
360 // Nested catalog mountpoints found in the moved directory structure are now
361 // links to nested catalogs of the newly created nested catalog.
362 // Move these references into the new nested catalog
363
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
690 MoveCatalogsToNested(GrandChildMountpoints, new_nested_catalog);
364 690 }
365
366
367 690 void WritableCatalog::MakeTransitionPoint(const string &mountpoint) {
368 // Find the directory entry to edit
369
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
690 DirectoryEntry transition_entry;
370
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
690 const bool retval = LookupPath(
371
1/2
✓ Branch 3 taken 690 times.
✗ Branch 4 not taken.
1380 PathString(mountpoint.data(), mountpoint.length()), &transition_entry);
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 690 times.
690 assert(retval);
373
374
2/4
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 690 times.
✗ Branch 5 not taken.
690 assert(transition_entry.IsDirectory()
375 && !transition_entry.IsNestedCatalogRoot());
376
377 690 transition_entry.set_is_nested_catalog_mountpoint(true);
378
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
690 UpdateEntry(transition_entry, mountpoint);
379 690 }
380
381
382 690 void WritableCatalog::MakeNestedRoot() {
383
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
690 DirectoryEntry root_entry;
384
2/4
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 690 times.
✗ Branch 5 not taken.
690 const bool retval = LookupPath(mountpoint(), &root_entry);
385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 690 times.
690 assert(retval);
386
387
2/4
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 690 times.
✗ Branch 5 not taken.
690 assert(root_entry.IsDirectory() && !root_entry.IsNestedCatalogMountpoint());
388
389 690 root_entry.set_is_nested_catalog_root(true);
390
3/6
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 690 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 690 times.
✗ Branch 8 not taken.
690 UpdateEntry(root_entry, mountpoint().ToString());
391 690 }
392
393
394 1353 void WritableCatalog::MoveToNestedRecursively(
395 const string directory,
396 WritableCatalog *new_nested_catalog,
397 vector<string> *grand_child_mountpoints) {
398 // After creating a new nested catalog we have to move all elements
399 // now contained by the new one. List and move them recursively.
400 1353 DirectoryEntryList listing;
401 1353 const bool resolve_magic_symlinks = false;
402
2/4
✓ Branch 1 taken 1353 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1353 times.
✗ Branch 5 not taken.
1353 bool retval = ListingPath(PathString(directory), &listing,
403 resolve_magic_symlinks);
404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 assert(retval);
405
406 // Go through the listing
407 1353 const XattrList empty_xattrs;
408 1353 for (DirectoryEntryList::const_iterator i = listing.begin(),
409 1353 iEnd = listing.end();
410
2/2
✓ Branch 1 taken 2921 times.
✓ Branch 2 taken 1353 times.
4274 i != iEnd;
411 2921 ++i) {
412
1/2
✓ Branch 2 taken 2921 times.
✗ Branch 3 not taken.
2921 const string full_path = i->GetFullPath(directory);
413
414 // The entries are first inserted into the new catalog
415
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2921 times.
2921 if (i->HasXattrs()) {
416 ✗ XattrList xattrs;
417 ✗ retval = LookupXattrsPath(PathString(full_path), &xattrs);
418 ✗ assert(retval);
419 ✗ assert(!xattrs.IsEmpty());
420 ✗ new_nested_catalog->AddEntry(*i, xattrs, full_path);
421 ✗ } else {
422
1/2
✓ Branch 2 taken 2921 times.
✗ Branch 3 not taken.
2921 new_nested_catalog->AddEntry(*i, empty_xattrs, full_path);
423 }
424
425 // Then we check if we have some special cases:
426
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2921 times.
2921 if (i->IsNestedCatalogMountpoint()) {
427 ✗ grand_child_mountpoints->push_back(full_path);
428
2/2
✓ Branch 2 taken 663 times.
✓ Branch 3 taken 2258 times.
2921 } else if (i->IsDirectory()) {
429 // Recurse deeper into the directory tree
430
2/4
✓ Branch 1 taken 663 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 663 times.
✗ Branch 5 not taken.
663 MoveToNestedRecursively(full_path, new_nested_catalog,
431 grand_child_mountpoints);
432
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2258 times.
2258 } else if (i->IsChunkedFile()) {
433 ✗ MoveFileChunksToNested(full_path, i->hash_algorithm(),
434 new_nested_catalog);
435 }
436
437 // Remove the entry from the current catalog
438
1/2
✓ Branch 1 taken 2921 times.
✗ Branch 2 not taken.
2921 RemoveEntry(full_path);
439 2921 }
440 1353 }
441
442
443 690 void WritableCatalog::MoveCatalogsToNested(
444 const vector<string> &nested_catalogs,
445 WritableCatalog *new_nested_catalog) {
446 1380 for (vector<string>::const_iterator i = nested_catalogs.begin(),
447 690 iEnd = nested_catalogs.end();
448
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 690 times.
690 i != iEnd;
449 ✗ ++i) {
450 ✗ shash::Any hash_nested;
451 uint64_t size_nested;
452 ✗ const bool retval = FindNested(PathString(*i), &hash_nested, &size_nested);
453 ✗ assert(retval);
454
455 ✗ Catalog *attached_reference = NULL;
456 ✗ RemoveNestedCatalog(*i, &attached_reference);
457
458 ✗ new_nested_catalog->InsertNestedCatalog(*i, attached_reference, hash_nested,
459 size_nested);
460 }
461 690 }
462
463
464 ✗ void WritableCatalog::MoveFileChunksToNested(
465 const std::string &full_path,
466 const shash::Algorithms algorithm,
467 WritableCatalog *new_nested_catalog) {
468 ✗ FileChunkList chunks;
469 ✗ ListPathChunks(PathString(full_path), algorithm, &chunks);
470 ✗ assert(chunks.size() > 0);
471
472 ✗ for (unsigned i = 0; i < chunks.size(); ++i) {
473 ✗ new_nested_catalog->AddFileChunk(full_path, *chunks.AtPtr(i));
474 }
475 }
476
477
478 /**
479 * Insert a nested catalog reference into this catalog.
480 * The attached catalog object of this mountpoint can be specified (optional)
481 * This way, the in-memory representation of the catalog tree is updated, too
482 * @param mountpoint the path to the catalog to add a reference to
483 * @param attached_reference can contain a reference to the attached catalog
484 * object of mountpoint
485 * @param content_hash can be set to safe a content hash together with the
486 * reference
487 */
488 803 void WritableCatalog::InsertNestedCatalog(const string &mountpoint,
489 Catalog *attached_reference,
490 const shash::Any content_hash,
491 const uint64_t size) {
492 803 const string hash_string = (!content_hash.IsNull()) ? content_hash.ToString()
493
6/12
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 690 times.
✓ Branch 3 taken 113 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 690 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 690 times.
✓ Branch 10 taken 113 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
803 : "";
494
495 803 SqlCatalog stmt(database(), "INSERT INTO nested_catalogs (path, sha1, size) "
496
2/4
✓ Branch 2 taken 803 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 803 times.
✗ Branch 6 not taken.
2409 "VALUES (:p, :sha1, :size);");
497
1/2
✓ Branch 1 taken 803 times.
✗ Branch 2 not taken.
803 const bool retval = stmt.BindText(1, mountpoint)
498
2/4
✓ Branch 1 taken 803 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 803 times.
✗ Branch 4 not taken.
803 && stmt.BindText(2, hash_string)
499
5/11
✓ Branch 0 taken 803 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 803 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 803 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 803 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 803 times.
✗ Branch 11 not taken.
1606 && stmt.BindInt64(3, size) && stmt.Execute();
500
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 803 times.
803 assert(retval);
501
502 // If a reference of the in-memory object of the newly referenced
503 // catalog was passed, we add this to our own children
504
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 803 times.
803 if (attached_reference != NULL)
505 ✗ AddChild(attached_reference);
506
507
1/2
✓ Branch 1 taken 803 times.
✗ Branch 2 not taken.
803 ResetNestedCatalogCacheUnprotected();
508
509 803 delta_counters_.self.nested_catalogs++;
510 803 }
511
512
513 /**
514 * Registers a snapshot in /.cvmfs/snapshots. Note that bind mountpoints are
515 * not universally handled: in Partition and MergeIntoParent, bind mountpoint
516 * handling is missing!
517 */
518 ✗ void WritableCatalog::InsertBindMountpoint(const string &mountpoint,
519 const shash::Any content_hash,
520 const uint64_t size) {
521 ✗ SqlCatalog stmt(database(),
522 "INSERT INTO bind_mountpoints (path, sha1, size) "
523 ✗ "VALUES (:p, :sha1, :size);");
524 ✗ const bool retval = stmt.BindText(1, mountpoint)
525 ✗ && stmt.BindText(2, content_hash.ToString())
526 ✗ && stmt.BindInt64(3, size) && stmt.Execute();
527 ✗ assert(retval);
528 }
529
530
531 /**
532 * Remove a nested catalog reference from the database.
533 * If the catalog 'mountpoint' is currently attached as a child, it will be
534 * removed, too (but not detached).
535 * @param[in] mountpoint the mountpoint of the nested catalog to dereference in
536 the database
537 * @param[out] attached_reference is set to the object of the attached child or
538 * to NULL
539 */
540 74 void WritableCatalog::RemoveNestedCatalog(const string &mountpoint,
541 Catalog **attached_reference) {
542
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 shash::Any dummy;
543 uint64_t dummy_size;
544
2/4
✓ Branch 3 taken 74 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 74 times.
✗ Branch 7 not taken.
74 bool retval = FindNested(PathString(mountpoint.data(), mountpoint.length()),
545 &dummy, &dummy_size);
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 assert(retval);
547
548
2/4
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 74 times.
✗ Branch 7 not taken.
148 SqlCatalog stmt(database(), "DELETE FROM nested_catalogs WHERE path = :p;");
549
4/9
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 74 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 74 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 74 times.
✗ Branch 9 not taken.
74 retval = stmt.BindText(1, mountpoint) && stmt.Execute();
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 assert(retval);
551
552 // If the reference was successfully deleted, we also have to check whether
553 // there is also an attached reference in our in-memory data.
554 // In this case we remove the child and return it through **attached_reference
555
2/4
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 74 times.
✗ Branch 5 not taken.
74 Catalog *child = FindChild(PathString(mountpoint));
556
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 36 times.
74 if (child != NULL)
557
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 RemoveChild(child);
558
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 if (attached_reference != NULL)
559 ✗ *attached_reference = child;
560
561
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 ResetNestedCatalogCacheUnprotected();
562
563 74 delta_counters_.self.nested_catalogs--;
564 74 }
565
566
567 /**
568 * Unregisters a snapshot from /.cvmfs/snapshots. Note that bind mountpoints
569 * are not universally handled: in Partition and MergeIntoParent, bind
570 * mountpoint handling is missing!
571 */
572 ✗ void WritableCatalog::RemoveBindMountpoint(const std::string &mountpoint) {
573 ✗ shash::Any dummy;
574 uint64_t dummy_size;
575 ✗ bool retval = FindNested(PathString(mountpoint.data(), mountpoint.length()),
576 &dummy, &dummy_size);
577 ✗ assert(retval);
578
579 ✗ SqlCatalog stmt(database(), "DELETE FROM bind_mountpoints WHERE path = :p;");
580 ✗ retval = stmt.BindText(1, mountpoint) && stmt.Execute();
581 ✗ assert(retval);
582 }
583
584
585 /**
586 * Updates the link to a nested catalog in the database.
587 * @param path the path of the nested catalog to update
588 * @param hash the hash to set the given nested catalog link to
589 * @param size the uncompressed catalog database file size
590 * @param child_counters the statistics counters of the nested catalog
591 */
592 651 void WritableCatalog::UpdateNestedCatalog(const std::string &path,
593 const shash::Any &hash,
594 const uint64_t size,
595 const DeltaCounters &child_counters) {
596 651 const MutexLockGuard guard(lock_);
597
1/2
✓ Branch 1 taken 651 times.
✗ Branch 2 not taken.
651 SetDirty();
598
599
1/2
✓ Branch 1 taken 651 times.
✗ Branch 2 not taken.
651 child_counters.PopulateToParent(&delta_counters_);
600
601
1/2
✓ Branch 1 taken 651 times.
✗ Branch 2 not taken.
651 const string hash_str = hash.ToString();
602 const string sql = "UPDATE nested_catalogs SET sha1 = :sha1, size = :size "
603
1/2
✓ Branch 2 taken 651 times.
✗ Branch 3 not taken.
651 "WHERE path = :path;";
604
1/2
✓ Branch 2 taken 651 times.
✗ Branch 3 not taken.
651 SqlCatalog stmt(database(), sql);
605
606
3/7
✓ Branch 1 taken 651 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 651 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 651 times.
✗ Branch 7 not taken.
1302 const bool retval = stmt.BindText(1, hash_str) && stmt.BindInt64(2, size)
607
5/11
✓ Branch 0 taken 651 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 651 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 651 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 651 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 651 times.
✗ Branch 11 not taken.
1302 && stmt.BindText(3, path) && stmt.Execute();
608
609
1/2
✓ Branch 1 taken 651 times.
✗ Branch 2 not taken.
651 ResetNestedCatalogCacheUnprotected();
610
611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 651 times.
651 assert(retval);
612 651 }
613
614
615 38 void WritableCatalog::MergeIntoParent() {
616
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 assert(!IsRoot() && HasParent());
617 38 WritableCatalog *parent = GetWritableParent();
618
619 38 CopyToParent();
620
621 // Copy the nested catalog references
622 38 CopyCatalogsToParent();
623
624 // Fix counters in parent
625 38 delta_counters_.PopulateToParent(&parent->delta_counters_);
626 38 Counters &counters = GetWritableCounters();
627 38 counters.ApplyDelta(delta_counters_);
628 38 counters.MergeIntoParent(&parent->delta_counters_);
629
630 // Remove the nested catalog reference for this nested catalog.
631 // From now on this catalog will be dangling!
632
2/4
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
38 parent->RemoveNestedCatalog(this->mountpoint().ToString(), NULL);
633 38 }
634
635
636 ✗ void WritableCatalog::RemoveFromParent() {
637 ✗ assert(!IsRoot() && HasParent());
638 ✗ WritableCatalog *parent = GetWritableParent();
639
640 // Remove the nested catalog reference for this nested catalog.
641 // From now on this catalog will be dangling!
642 ✗ parent->RemoveNestedCatalog(this->mountpoint().ToString(), NULL);
643 ✗ parent->delta_counters_.RemoveFromSubtree(
644 ✗ Counters::Diff(Counters(), GetCounters()));
645 }
646
647
648 38 void WritableCatalog::CopyCatalogsToParent() {
649
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 WritableCatalog *parent = GetWritableParent();
650
651 // Obtain a list of all nested catalog references
652
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const NestedCatalogList nested_catalog_references = ListOwnNestedCatalogs();
653
654 // Go through the list and update the databases
655 // simultaneously we are checking if the referenced catalogs are currently
656 // attached and update the in-memory data structures as well
657 76 for (NestedCatalogList::const_iterator i = nested_catalog_references.begin(),
658 38 iEnd = nested_catalog_references.end();
659
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 i != iEnd;
660 ✗ ++i) {
661 ✗ Catalog *child = FindChild(i->mountpoint);
662 ✗ parent->InsertNestedCatalog(i->mountpoint.ToString(), child, i->hash,
663 ✗ i->size);
664 ✗ parent->delta_counters_.self.nested_catalogs--; // Will be fixed later
665 }
666 38 }
667
668 38 void WritableCatalog::CopyToParent() {
669 // We could simply copy all entries from this database to the 'other' database
670 // BUT: 1. this would create collisions in hardlink group IDs.
671 // therefore we first update all hardlink group IDs to fit behind the
672 // ones in the 'other' database
673 // 2. the root entry of the nested catalog is present twice:
674 // 1. in the parent directory (as mount point) and
675 // 2. in the nested catalog (as root entry)
676 // therefore we delete the mount point from the parent before merging
677
678
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 WritableCatalog *parent = GetWritableParent();
679
680 // Update hardlink group IDs in this nested catalog.
681 // To avoid collisions we add the maximal present hardlink group ID in parent
682 // to all hardlink group IDs in the nested catalog.
683
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const uint64_t offset = static_cast<uint64_t>(parent->GetMaxLinkId()) << 32;
684 const string update_link_ids = "UPDATE catalog SET hardlinks = hardlinks + "
685
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
76 + StringifyInt(offset)
686
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 + " WHERE hardlinks > (1 << 32);";
687
688
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 SqlCatalog sql_update_link_ids(database(), update_link_ids);
689
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 bool retval = sql_update_link_ids.Execute();
690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(retval);
691
692 // Remove the nested catalog root.
693 // It is already present in the parent.
694
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
38 RemoveEntry(this->mountpoint().ToString());
695
696 // Now copy all DirectoryEntries to the 'other' catalog.
697 // There will be no data collisions, as we resolved them beforehand
698
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if (dirty_)
699
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 Commit();
700
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 26 times.
38 if (parent->dirty_)
701
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 parent->Commit();
702
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
114 SqlCatalog sql_attach(database(), "ATTACH '" + parent->database_path()
703
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
76 + "' "
704
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 "AS other;");
705
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 retval = sql_attach.Execute();
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(retval);
707 // The two databases can have a different physical column order: fresh
708 // databases are created with mtimens between mtime and flags, while
709 // databases migrated from schema revision < 7 have mtimens appended at the
710 // end (ALTER TABLE can only append). A positional "SELECT *" copy would
711 // silently shuffle the values into the wrong columns, so the columns have
712 // to be named explicitly.
713 const string catalog_columns =
714 "md5path_1, md5path_2, parent_1, parent_2, hardlinks, hash, size, "
715
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 "mode, mtime, mtimens, flags, name, symlink, uid, gid, xattr";
716
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 retval = SqlCatalog(database(),
717
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 "INSERT INTO other.catalog (" + catalog_columns
718
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
76 + ") SELECT " + catalog_columns
719
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
76 + " FROM main.catalog;")
720
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 .Execute();
721
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(retval);
722
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 const string chunks_columns = "md5path_1, md5path_2, offset, size, hash";
723
1/2
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 retval = SqlCatalog(database(),
724
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 "INSERT INTO other.chunks (" + chunks_columns
725
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
76 + ") SELECT " + chunks_columns + " FROM main.chunks;")
726
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 .Execute();
727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(retval);
728
3/6
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 38 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 38 times.
✗ Branch 10 not taken.
38 retval = SqlCatalog(database(), "DETACH other;").Execute();
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(retval);
730
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 parent->SetDirty();
731
732 // Change the just copied nested catalog root to an ordinary directory
733 // (the nested catalog is merged into it's parent)
734
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 DirectoryEntry old_root_entry;
735
2/4
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 retval = parent->LookupPath(this->mountpoint(), &old_root_entry);
736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(retval);
737
738
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
38 assert(old_root_entry.IsDirectory()
739 && old_root_entry.IsNestedCatalogMountpoint()
740 && !old_root_entry.IsNestedCatalogRoot());
741
742 // Remove the nested catalog root mark
743 38 old_root_entry.set_is_nested_catalog_mountpoint(false);
744
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
38 parent->UpdateEntry(old_root_entry, this->mountpoint().ToString());
745 38 }
746
747
748 /**
749 * Writes delta_counters_ to the database.
750 */
751 1881 void WritableCatalog::UpdateCounters() {
752 1881 const bool retval = delta_counters_.WriteToDatabase(database())
753
2/4
✓ Branch 0 taken 1881 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1881 times.
✗ Branch 4 not taken.
1881 && ReadCatalogCounters();
754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1881 times.
1881 assert(retval);
755 1881 }
756
757
758 /**
759 * Checks if the database of this catalogs needs cleanup and defragments it
760 * if necessary
761 */
762 1191 void WritableCatalog::VacuumDatabaseIfNecessary() {
763 1191 const CatalogDatabase &db = database();
764 1191 bool needs_defragmentation = false;
765 1191 double ratio = 0.0;
766 1191 std::string reason;
767 1191 const MutexLockGuard m(lock_);
768
769
2/4
✓ Branch 1 taken 1191 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1191 times.
1191 if ((ratio = db.GetFreePageRatio()) > kMaximalFreePageRatio) {
770 ✗ needs_defragmentation = true;
771 ✗ reason = "free pages";
772
3/4
✓ Branch 1 taken 1191 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 314 times.
✓ Branch 4 taken 877 times.
1191 } else if ((ratio = db.GetRowIdWasteRatio()) > kMaximalRowIdWasteRatio) {
773 314 needs_defragmentation = true;
774
1/2
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
314 reason = "wasted row IDs";
775 }
776
777
2/2
✓ Branch 0 taken 314 times.
✓ Branch 1 taken 877 times.
1191 if (needs_defragmentation) {
778
3/14
✓ Branch 1 taken 170 times.
✓ Branch 2 taken 144 times.
✓ Branch 5 taken 314 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
772 LogCvmfs(kLogCatalog, kLogStdout | kLogNoLinebreak,
779 "Note: Catalog at %s gets defragmented (%.2f%% %s)... ",
780
3/6
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✓ Branch 6 taken 170 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
772 (IsRoot()) ? "/" : mountpoint().c_str(), ratio * 100.0,
781 reason.c_str());
782
2/4
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 314 times.
314 if (!db.Vacuum()) {
783 ✗ PANIC(kLogStderr, "failed (SQLite: %s)", db.GetLastErrorMsg().c_str());
784 }
785
1/2
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
314 LogCvmfs(kLogCatalog, kLogStdout, "done");
786 }
787 1191 }
788
789 } // namespace catalog
790