GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_rw.cc
Date: 2026-08-30 02:40:36
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 4280 WritableCatalog::WritableCatalog(const string &path,
25 const shash::Any &catalog_hash,
26 Catalog *parent,
27 4280 const bool is_not_root)
28 8560 : Catalog(PathString(path.data(), path.length()),
29 catalog_hash, // This is 0 for a newly created catalog!
30 parent,
31 is_not_root)
32 4280 , sql_insert_(NULL)
33 4280 , sql_unlink_(NULL)
34 4280 , sql_touch_(NULL)
35 4280 , sql_update_(NULL)
36 4280 , sql_chunk_insert_(NULL)
37 4280 , sql_chunks_remove_(NULL)
38 4280 , sql_chunks_count_(NULL)
39 4280 , sql_max_link_id_(NULL)
40 4280 , sql_inc_linkcount_(NULL)
41
2/4
✓ Branch 2 taken 4280 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4280 times.
✗ Branch 6 not taken.
12840 , dirty_(false) {
42 4280 atomic_init32(&dirty_children_);
43 4280 }
44
45
46 1270 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 1270 times.
✗ Branch 3 not taken.
1270 parent, is_not_root);
53 1270 const bool successful_init = catalog->InitStandalone(file);
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1270 times.
1270 if (!successful_init) {
55 delete catalog;
56 return NULL;
57 }
58 1270 return catalog;
59 }
60
61
62 16912 WritableCatalog::~WritableCatalog() {
63 // CAUTION HOT!
64 // (see Catalog.h - near the definition of FinalizePreparedStatements)
65 8456 FinalizePreparedStatements();
66 16912 }
67
68
69 3886 void WritableCatalog::Transaction() {
70
1/3
✓ Branch 2 taken 3886 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3886 LogCvmfs(kLogCatalog, kLogVerboseMsg, "opening SQLite transaction for '%s'",
71 7772 mountpoint().c_str());
72 3886 const bool retval = database().BeginTransaction();
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3886 times.
3886 assert(retval == true);
74 3886 }
75
76
77 3561 void WritableCatalog::Commit() {
78
1/3
✓ Branch 2 taken 3561 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3561 LogCvmfs(kLogCatalog, kLogVerboseMsg, "closing SQLite transaction for '%s'",
79 7122 mountpoint().c_str());
80 3561 const bool retval = database().CommitTransaction();
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3561 times.
3561 assert(retval == true);
82 3561 dirty_ = false;
83 3561 }
84
85
86 4280 void WritableCatalog::InitPreparedStatements() {
87 4280 Catalog::InitPreparedStatements(); // polymorphism: up call
88
89
2/4
✓ Branch 2 taken 4280 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 4280 times.
✗ Branch 7 not taken.
8560 const bool retval = SqlCatalog(database(), "PRAGMA foreign_keys = ON;")
90
1/2
✓ Branch 1 taken 4280 times.
✗ Branch 2 not taken.
4280 .Execute();
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4280 times.
4280 assert(retval);
92
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_insert_ = new SqlDirentInsert(database());
93
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_unlink_ = new SqlDirentUnlink(database());
94
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_touch_ = new SqlDirentTouch(database());
95
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_update_ = new SqlDirentUpdate(database());
96
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_chunk_insert_ = new SqlChunkInsert(database());
97
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_chunks_remove_ = new SqlChunksRemove(database());
98
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_chunks_count_ = new SqlChunksCount(database());
99
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_max_link_id_ = new SqlMaxHardlinkGroup(database());
100
1/2
✓ Branch 3 taken 4280 times.
✗ Branch 4 not taken.
4280 sql_inc_linkcount_ = new SqlIncLinkcount(database());
101 4280 }
102
103
104 4228 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 4228 times.
✗ Branch 1 not taken.
4228 delete sql_insert_;
108
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_unlink_;
109
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_touch_;
110
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_update_;
111
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_chunk_insert_;
112
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_chunks_remove_;
113
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_chunks_count_;
114
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_max_link_id_;
115
1/2
✓ Branch 0 taken 4228 times.
✗ Branch 1 not taken.
4228 delete sql_inc_linkcount_;
116 4228 }
117
118
119 /**
120 * Find out the maximal hardlink group id in this catalog.
121 */
122 158 uint32_t WritableCatalog::GetMaxLinkId() const {
123 158 int result = -1;
124
125
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 if (sql_max_link_id_->FetchRow()) {
126 158 result = sql_max_link_id_->GetMaxGroupId();
127 }
128 158 sql_max_link_id_->Reset();
129
130 158 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 19532 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 19532 times.
✗ Branch 2 not taken.
19532 SetDirty();
145
146
1/6
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19532 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
19532 LogCvmfs(kLogCatalog, kLogVerboseMsg, "add entry '%s' to '%s'",
147
1/2
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
39064 entry_path.c_str(), mountpoint().c_str());
148
149
1/2
✓ Branch 2 taken 19532 times.
✗ Branch 3 not taken.
19532 const shash::Md5 path_hash((shash::AsciiPtr(entry_path)));
150
1/2
✓ Branch 2 taken 19532 times.
✗ Branch 3 not taken.
19532 const shash::Md5 parent_hash((shash::AsciiPtr(parent_path)));
151
1/2
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
19532 DirectoryEntry effective_entry(entry);
152 19532 effective_entry.set_has_xattrs(!xattrs.IsEmpty());
153
154
1/2
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
19532 bool retval = sql_insert_->BindPathHash(path_hash)
155
2/4
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 19532 times.
✗ Branch 4 not taken.
19532 && sql_insert_->BindParentPathHash(parent_hash)
156
3/6
✓ Branch 0 taken 19532 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 19532 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19532 times.
✗ Branch 6 not taken.
39064 && sql_insert_->BindDirent(effective_entry);
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19532 times.
19532 assert(retval);
158
1/2
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
19532 if (xattrs.IsEmpty()) {
159
1/2
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
19532 retval = sql_insert_->BindXattrEmpty();
160 } else {
161 retval = sql_insert_->BindXattr(xattrs);
162 }
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19532 times.
19532 assert(retval);
164
1/2
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
19532 retval = sql_insert_->Execute();
165
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19532 times.
19532 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 19532 times.
✗ Branch 2 not taken.
19532 sql_insert_->Reset();
171
172
1/2
✓ Branch 1 taken 19532 times.
✗ Branch 2 not taken.
19532 delta_counters_.Increment(effective_entry);
173 19532 }
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 4462 void WritableCatalog::RemoveEntry(const string &file_path) {
183
1/2
✓ Branch 1 taken 4462 times.
✗ Branch 2 not taken.
4462 DirectoryEntry entry;
184
2/4
✓ Branch 1 taken 4462 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4462 times.
✗ Branch 5 not taken.
4462 bool retval = LookupPath(PathString(file_path), &entry);
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4462 times.
4462 assert(retval);
186
187
1/2
✓ Branch 1 taken 4462 times.
✗ Branch 2 not taken.
4462 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 4462 times.
4462 if (entry.IsChunkedFile()) {
191 RemoveFileChunks(file_path);
192 }
193
194 // remove the entry itself
195
1/2
✓ Branch 2 taken 4462 times.
✗ Branch 3 not taken.
4462 const shash::Md5 path_hash = shash::Md5(shash::AsciiPtr(file_path));
196
4/9
✓ Branch 1 taken 4462 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4462 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4462 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4462 times.
✗ Branch 9 not taken.
4462 retval = sql_unlink_->BindPathHash(path_hash) && sql_unlink_->Execute();
197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4462 times.
4462 assert(retval);
198
1/2
✓ Branch 1 taken 4462 times.
✗ Branch 2 not taken.
4462 sql_unlink_->Reset();
199
200
1/2
✓ Branch 1 taken 4462 times.
✗ Branch 2 not taken.
4462 delta_counters_.Decrement(entry);
201 4462 }
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 139 void WritableCatalog::TouchEntry(const DirectoryEntryBase &entry,
219 const XattrList &xattrs,
220 const shash::Md5 &path_hash) {
221
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 SetDirty();
222
223
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 catalog::DirectoryEntry prev_entry;
224
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 bool retval = LookupMd5Path(path_hash, &prev_entry);
225
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 assert(retval);
226
227
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 retval = sql_touch_->BindPathHash(path_hash)
228
3/6
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 139 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 139 times.
✗ Branch 6 not taken.
139 && sql_touch_->BindDirentBase(entry);
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 assert(retval);
230
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 if (xattrs.IsEmpty()) {
231
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 retval = sql_touch_->BindXattrEmpty();
232
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 139 times.
139 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 139 times.
139 assert(retval);
240
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 retval = sql_touch_->Execute();
241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 assert(retval);
242
1/2
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
139 sql_touch_->Reset();
243 139 }
244
245
246 7332 void WritableCatalog::UpdateEntry(const DirectoryEntry &entry,
247 const shash::Md5 &path_hash) {
248 7332 SetDirty();
249
250 7332 const bool retval = sql_update_->BindPathHash(path_hash)
251
1/2
✓ Branch 1 taken 7332 times.
✗ Branch 2 not taken.
7332 && sql_update_->BindDirent(entry)
252
2/4
✓ Branch 0 taken 7332 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7332 times.
✗ Branch 4 not taken.
14664 && sql_update_->Execute();
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7332 times.
7332 assert(retval);
254 7332 sql_update_->Reset();
255 7332 }
256
257 586 void WritableCatalog::AddFileChunk(const std::string &entry_path,
258 const FileChunk &chunk) {
259
1/2
✓ Branch 1 taken 586 times.
✗ Branch 2 not taken.
586 SetDirty();
260
261
1/2
✓ Branch 2 taken 586 times.
✗ Branch 3 not taken.
586 const shash::Md5 path_hash((shash::AsciiPtr(entry_path)));
262
263
1/4
✓ Branch 3 taken 586 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
586 LogCvmfs(kLogCatalog, kLogVerboseMsg,
264 "adding chunk for %s from offset %ld "
265 "and chunk size: %ld bytes",
266 586 entry_path.c_str(), chunk.offset(), chunk.offset() + chunk.size());
267
268 586 delta_counters_.self.file_chunks++;
269
270
1/2
✓ Branch 1 taken 586 times.
✗ Branch 2 not taken.
586 const bool retval = sql_chunk_insert_->BindPathHash(path_hash)
271
2/4
✓ Branch 1 taken 586 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 586 times.
✗ Branch 4 not taken.
586 && sql_chunk_insert_->BindFileChunk(chunk)
272
3/6
✓ Branch 0 taken 586 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 586 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 586 times.
✗ Branch 6 not taken.
1172 && sql_chunk_insert_->Execute();
273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 586 times.
586 assert(retval);
274
1/2
✓ Branch 1 taken 586 times.
✗ Branch 2 not taken.
586 sql_chunk_insert_->Reset();
275 586 }
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 7699 void WritableCatalog::UpdateLastModified() {
306
2/4
✓ Branch 4 taken 7699 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7699 times.
✗ Branch 8 not taken.
7699 database().SetProperty("last_modified", static_cast<uint64_t>(time(NULL)));
307 7699 }
308
309
310 /**
311 * Increments the revision of the catalog in the database.
312 */
313 2133 void WritableCatalog::IncrementRevision() { SetRevision(GetRevision() + 1); }
314
315
316 2133 void WritableCatalog::SetRevision(const uint64_t new_revision) {
317
2/4
✓ Branch 3 taken 2133 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2133 times.
✗ Branch 7 not taken.
2133 database().SetProperty("revision", new_revision);
318 2133 }
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 2133 void WritableCatalog::SetPreviousRevision(const shash::Any &hash) {
340
2/4
✓ Branch 4 taken 2133 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2133 times.
✗ Branch 8 not taken.
2133 database().SetProperty("previous_revision", hash.ToString());
341 2133 }
342
343
344 /**
345 * Moves a subtree from this catalog into a just created nested catalog.
346 */
347 1057 void WritableCatalog::Partition(WritableCatalog *new_nested_catalog) {
348 // Create connection between parent and child catalogs
349
3/6
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1057 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1057 times.
✗ Branch 8 not taken.
1057 MakeTransitionPoint(new_nested_catalog->mountpoint().ToString());
350
1/2
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
1057 new_nested_catalog->MakeNestedRoot();
351 1057 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 1057 vector<string> GrandChildMountpoints;
357
3/6
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1057 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1057 times.
✗ Branch 8 not taken.
1057 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 1057 times.
✗ Branch 2 not taken.
1057 MoveCatalogsToNested(GrandChildMountpoints, new_nested_catalog);
364 1057 }
365
366
367 1057 void WritableCatalog::MakeTransitionPoint(const string &mountpoint) {
368 // Find the directory entry to edit
369
1/2
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
1057 DirectoryEntry transition_entry;
370
1/2
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
1057 const bool retval = LookupPath(
371
1/2
✓ Branch 3 taken 1057 times.
✗ Branch 4 not taken.
2114 PathString(mountpoint.data(), mountpoint.length()), &transition_entry);
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1057 times.
1057 assert(retval);
373
374
2/4
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1057 times.
✗ Branch 5 not taken.
1057 assert(transition_entry.IsDirectory()
375 && !transition_entry.IsNestedCatalogRoot());
376
377 1057 transition_entry.set_is_nested_catalog_mountpoint(true);
378
1/2
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
1057 UpdateEntry(transition_entry, mountpoint);
379 1057 }
380
381
382 1057 void WritableCatalog::MakeNestedRoot() {
383
1/2
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
1057 DirectoryEntry root_entry;
384
2/4
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1057 times.
✗ Branch 5 not taken.
1057 const bool retval = LookupPath(mountpoint(), &root_entry);
385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1057 times.
1057 assert(retval);
386
387
2/4
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1057 times.
✗ Branch 5 not taken.
1057 assert(root_entry.IsDirectory() && !root_entry.IsNestedCatalogMountpoint());
388
389 1057 root_entry.set_is_nested_catalog_root(true);
390
3/6
✓ Branch 1 taken 1057 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1057 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1057 times.
✗ Branch 8 not taken.
1057 UpdateEntry(root_entry, mountpoint().ToString());
391 1057 }
392
393
394 1858 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 1858 DirectoryEntryList listing;
401 1858 const bool resolve_magic_symlinks = false;
402
2/4
✓ Branch 1 taken 1858 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1858 times.
✗ Branch 5 not taken.
1858 bool retval = ListingPath(PathString(directory), &listing,
403 resolve_magic_symlinks);
404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1858 times.
1858 assert(retval);
405
406 // Go through the listing
407 1858 const XattrList empty_xattrs;
408 1858 for (DirectoryEntryList::const_iterator i = listing.begin(),
409 1858 iEnd = listing.end();
410
2/2
✓ Branch 1 taken 3940 times.
✓ Branch 2 taken 1858 times.
5798 i != iEnd;
411 3940 ++i) {
412
1/2
✓ Branch 2 taken 3940 times.
✗ Branch 3 not taken.
3940 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 3940 times.
3940 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 3940 times.
✗ Branch 3 not taken.
3940 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 3940 times.
3940 if (i->IsNestedCatalogMountpoint()) {
427 grand_child_mountpoints->push_back(full_path);
428
2/2
✓ Branch 2 taken 801 times.
✓ Branch 3 taken 3139 times.
3940 } else if (i->IsDirectory()) {
429 // Recurse deeper into the directory tree
430
2/4
✓ Branch 1 taken 801 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 801 times.
✗ Branch 5 not taken.
801 MoveToNestedRecursively(full_path, new_nested_catalog,
431 grand_child_mountpoints);
432
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3139 times.
3139 } 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 3940 times.
✗ Branch 2 not taken.
3940 RemoveEntry(full_path);
439 3940 }
440 1858 }
441
442
443 1057 void WritableCatalog::MoveCatalogsToNested(
444 const vector<string> &nested_catalogs,
445 WritableCatalog *new_nested_catalog) {
446 2114 for (vector<string>::const_iterator i = nested_catalogs.begin(),
447 1057 iEnd = nested_catalogs.end();
448
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1057 times.
1057 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 1057 }
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 1763 void WritableCatalog::InsertNestedCatalog(const string &mountpoint,
489 Catalog *attached_reference,
490 const shash::Any content_hash,
491 const uint64_t size) {
492 1763 const string hash_string = (!content_hash.IsNull()) ? content_hash.ToString()
493
6/12
✓ Branch 0 taken 706 times.
✓ Branch 1 taken 1057 times.
✓ Branch 3 taken 706 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1057 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1057 times.
✓ Branch 10 taken 706 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
1763 : "";
494
495 1763 SqlCatalog stmt(database(), "INSERT INTO nested_catalogs (path, sha1, size) "
496
2/4
✓ Branch 2 taken 1763 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1763 times.
✗ Branch 6 not taken.
5289 "VALUES (:p, :sha1, :size);");
497
1/2
✓ Branch 1 taken 1763 times.
✗ Branch 2 not taken.
1763 const bool retval = stmt.BindText(1, mountpoint)
498
2/4
✓ Branch 1 taken 1763 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1763 times.
✗ Branch 4 not taken.
1763 && stmt.BindText(2, hash_string)
499
5/11
✓ Branch 0 taken 1763 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1763 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1763 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1763 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1763 times.
✗ Branch 11 not taken.
3526 && stmt.BindInt64(3, size) && stmt.Execute();
500
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1763 times.
1763 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 1763 times.
1763 if (attached_reference != NULL)
505 AddChild(attached_reference);
506
507
1/2
✓ Branch 1 taken 1763 times.
✗ Branch 2 not taken.
1763 ResetNestedCatalogCacheUnprotected();
508
509 1763 delta_counters_.self.nested_catalogs++;
510 1763 }
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 248 void WritableCatalog::RemoveNestedCatalog(const string &mountpoint,
541 Catalog **attached_reference) {
542
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 shash::Any dummy;
543 uint64_t dummy_size;
544
2/4
✓ Branch 3 taken 248 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 248 times.
✗ Branch 7 not taken.
248 bool retval = FindNested(PathString(mountpoint.data(), mountpoint.length()),
545 &dummy, &dummy_size);
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
248 assert(retval);
547
548
2/4
✓ Branch 2 taken 248 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 248 times.
✗ Branch 7 not taken.
496 SqlCatalog stmt(database(), "DELETE FROM nested_catalogs WHERE path = :p;");
549
4/9
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 248 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 248 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 248 times.
✗ Branch 9 not taken.
248 retval = stmt.BindText(1, mountpoint) && stmt.Execute();
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
248 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 248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 248 times.
✗ Branch 5 not taken.
248 Catalog *child = FindChild(PathString(mountpoint));
556
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 90 times.
248 if (child != NULL)
557
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 RemoveChild(child);
558
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
248 if (attached_reference != NULL)
559 *attached_reference = child;
560
561
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 ResetNestedCatalogCacheUnprotected();
562
563 248 delta_counters_.self.nested_catalogs--;
564 248 }
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 970 void WritableCatalog::UpdateNestedCatalog(const std::string &path,
593 const shash::Any &hash,
594 const uint64_t size,
595 const DeltaCounters &child_counters) {
596 970 const MutexLockGuard guard(lock_);
597
1/2
✓ Branch 1 taken 970 times.
✗ Branch 2 not taken.
970 SetDirty();
598
599
1/2
✓ Branch 1 taken 970 times.
✗ Branch 2 not taken.
970 child_counters.PopulateToParent(&delta_counters_);
600
601
1/2
✓ Branch 1 taken 970 times.
✗ Branch 2 not taken.
970 const string hash_str = hash.ToString();
602 const string sql = "UPDATE nested_catalogs SET sha1 = :sha1, size = :size "
603
1/2
✓ Branch 2 taken 970 times.
✗ Branch 3 not taken.
970 "WHERE path = :path;";
604
1/2
✓ Branch 2 taken 970 times.
✗ Branch 3 not taken.
970 SqlCatalog stmt(database(), sql);
605
606
3/7
✓ Branch 1 taken 970 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 970 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 970 times.
✗ Branch 7 not taken.
1940 const bool retval = stmt.BindText(1, hash_str) && stmt.BindInt64(2, size)
607
5/11
✓ Branch 0 taken 970 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 970 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 970 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 970 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 970 times.
✗ Branch 11 not taken.
1940 && stmt.BindText(3, path) && stmt.Execute();
608
609
1/2
✓ Branch 1 taken 970 times.
✗ Branch 2 not taken.
970 ResetNestedCatalogCacheUnprotected();
610
611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 970 times.
970 assert(retval);
612 970 }
613
614
615 158 void WritableCatalog::MergeIntoParent() {
616
2/4
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
158 assert(!IsRoot() && HasParent());
617 158 WritableCatalog *parent = GetWritableParent();
618
619 158 CopyToParent();
620
621 // Copy the nested catalog references
622 158 CopyCatalogsToParent();
623
624 // Fix counters in parent
625 158 delta_counters_.PopulateToParent(&parent->delta_counters_);
626 158 Counters &counters = GetWritableCounters();
627 158 counters.ApplyDelta(delta_counters_);
628 158 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 158 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 158 times.
✗ Branch 6 not taken.
158 parent->RemoveNestedCatalog(this->mountpoint().ToString(), NULL);
633 158 }
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 158 void WritableCatalog::CopyCatalogsToParent() {
649
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 WritableCatalog *parent = GetWritableParent();
650
651 // Obtain a list of all nested catalog references
652
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 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 316 for (NestedCatalogList::const_iterator i = nested_catalog_references.begin(),
658 158 iEnd = nested_catalog_references.end();
659
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 158 times.
158 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 158 }
667
668 158 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 158 times.
✗ Branch 2 not taken.
158 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 158 times.
✗ Branch 2 not taken.
158 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 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
316 + StringifyInt(offset)
686
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 + " WHERE hardlinks > (1 << 32);";
687
688
1/2
✓ Branch 2 taken 158 times.
✗ Branch 3 not taken.
158 SqlCatalog sql_update_link_ids(database(), update_link_ids);
689
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 bool retval = sql_update_link_ids.Execute();
690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 assert(retval);
691
692 // Remove the nested catalog root.
693 // It is already present in the parent.
694
3/6
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 158 times.
✗ Branch 8 not taken.
158 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 158 times.
✗ Branch 1 not taken.
158 if (dirty_)
699
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 Commit();
700
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 60 times.
158 if (parent->dirty_)
701
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 parent->Commit();
702
2/4
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
474 SqlCatalog sql_attach(database(), "ATTACH '" + parent->database_path()
703
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
316 + "' "
704
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 "AS other;");
705
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 retval = sql_attach.Execute();
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 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 158 times.
✗ Branch 3 not taken.
158 "mode, mtime, mtimens, flags, name, symlink, uid, gid, xattr";
716
1/2
✓ Branch 2 taken 158 times.
✗ Branch 3 not taken.
158 retval = SqlCatalog(database(),
717
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 "INSERT INTO other.catalog (" + catalog_columns
718
2/4
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
316 + ") SELECT " + catalog_columns
719
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
316 + " FROM main.catalog;")
720
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 .Execute();
721
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 assert(retval);
722
1/2
✓ Branch 2 taken 158 times.
✗ Branch 3 not taken.
158 const string chunks_columns = "md5path_1, md5path_2, offset, size, hash";
723
1/2
✓ Branch 2 taken 158 times.
✗ Branch 3 not taken.
158 retval = SqlCatalog(database(),
724
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 "INSERT INTO other.chunks (" + chunks_columns
725
3/6
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 158 times.
✗ Branch 8 not taken.
316 + ") SELECT " + chunks_columns + " FROM main.chunks;")
726
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 .Execute();
727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 assert(retval);
728
3/6
✓ Branch 2 taken 158 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 158 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 158 times.
✗ Branch 10 not taken.
158 retval = SqlCatalog(database(), "DETACH other;").Execute();
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 assert(retval);
730
1/2
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
158 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 158 times.
✗ Branch 2 not taken.
158 DirectoryEntry old_root_entry;
735
2/4
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
158 retval = parent->LookupPath(this->mountpoint(), &old_root_entry);
736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 assert(retval);
737
738
3/6
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 158 times.
✗ Branch 8 not taken.
158 assert(old_root_entry.IsDirectory()
739 && old_root_entry.IsNestedCatalogMountpoint()
740 && !old_root_entry.IsNestedCatalogRoot());
741
742 // Remove the nested catalog root mark
743 158 old_root_entry.set_is_nested_catalog_mountpoint(false);
744
3/6
✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 158 times.
✗ Branch 8 not taken.
158 parent->UpdateEntry(old_root_entry, this->mountpoint().ToString());
745 158 }
746
747
748 /**
749 * Writes delta_counters_ to the database.
750 */
751 3190 void WritableCatalog::UpdateCounters() {
752 3190 const bool retval = delta_counters_.WriteToDatabase(database())
753
2/4
✓ Branch 0 taken 3190 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3190 times.
✗ Branch 4 not taken.
3190 && ReadCatalogCounters();
754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3190 times.
3190 assert(retval);
755 3190 }
756
757
758 /**
759 * Checks if the database of this catalogs needs cleanup and defragments it
760 * if necessary
761 */
762 2133 void WritableCatalog::VacuumDatabaseIfNecessary() {
763 2133 const CatalogDatabase &db = database();
764 2133 bool needs_defragmentation = false;
765 2133 double ratio = 0.0;
766 2133 std::string reason;
767 2133 const MutexLockGuard m(lock_);
768
769
2/4
✓ Branch 1 taken 2133 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2133 times.
2133 if ((ratio = db.GetFreePageRatio()) > kMaximalFreePageRatio) {
770 needs_defragmentation = true;
771 reason = "free pages";
772
3/4
✓ Branch 1 taken 2133 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 436 times.
✓ Branch 4 taken 1697 times.
2133 } else if ((ratio = db.GetRowIdWasteRatio()) > kMaximalRowIdWasteRatio) {
773 436 needs_defragmentation = true;
774
1/2
✓ Branch 1 taken 436 times.
✗ Branch 2 not taken.
436 reason = "wasted row IDs";
775 }
776
777
2/2
✓ Branch 0 taken 436 times.
✓ Branch 1 taken 1697 times.
2133 if (needs_defragmentation) {
778
3/14
✓ Branch 1 taken 346 times.
✓ Branch 2 taken 90 times.
✓ Branch 5 taken 436 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.
962 LogCvmfs(kLogCatalog, kLogStdout | kLogNoLinebreak,
779 "Note: Catalog at %s gets defragmented (%.2f%% %s)... ",
780
3/6
✓ Branch 2 taken 90 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 90 times.
✓ Branch 6 taken 346 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
962 (IsRoot()) ? "/" : mountpoint().c_str(), ratio * 100.0,
781 reason.c_str());
782
2/4
✓ Branch 1 taken 436 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 436 times.
436 if (!db.Vacuum()) {
783 PANIC(kLogStderr, "failed (SQLite: %s)", db.GetLastErrorMsg().c_str());
784 }
785
1/2
✓ Branch 1 taken 436 times.
✗ Branch 2 not taken.
436 LogCvmfs(kLogCatalog, kLogStdout, "done");
786 }
787 2133 }
788
789 } // namespace catalog
790