GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog.cc
Date: 2026-10-04 02:40:33
Exec Total Coverage
Lines: 407 439 92.7%
Branches: 324 606 53.5%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "catalog.h"
6
7 #include <alloca.h>
8
9 #include <cassert>
10
11 #include "catalog_mgr.h"
12 #include "util/logging.h"
13 #include "util/smalloc.h"
14
15 using namespace std; // NOLINT
16
17 namespace catalog {
18
19 const shash::Md5 Catalog::kMd5PathEmpty("", 0);
20
21
22 /**
23 * Open a catalog outside the framework of a catalog manager.
24 */
25 208 Catalog *Catalog::AttachFreely(const string &imaginary_mountpoint,
26 const string &file,
27 const shash::Any &catalog_hash,
28 Catalog *parent,
29 const bool is_nested) {
30 Catalog *catalog = new Catalog(
31 208 PathString(imaginary_mountpoint.data(), imaginary_mountpoint.length()),
32 catalog_hash,
33 parent,
34
2/4
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 208 times.
✗ Branch 5 not taken.
208 is_nested);
35 208 const bool successful_init = catalog->InitStandalone(file);
36
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 204 times.
208 if (!successful_init) {
37
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 delete catalog;
38 4 return NULL;
39 }
40 204 return catalog;
41 }
42
43
44 2714 Catalog::Catalog(const PathString &mountpoint,
45 const shash::Any &catalog_hash,
46 Catalog *parent,
47 2714 const bool is_nested)
48 2714 : catalog_hash_(catalog_hash)
49
1/2
✓ Branch 1 taken 2714 times.
✗ Branch 2 not taken.
2714 , mountpoint_(mountpoint)
50
1/2
✓ Branch 1 taken 2714 times.
✗ Branch 2 not taken.
2714 , is_regular_mountpoint_(mountpoint_ == root_prefix_)
51 2714 , volatile_flag_(false)
52
3/4
✓ Branch 0 taken 1592 times.
✓ Branch 1 taken 1122 times.
✓ Branch 2 taken 1592 times.
✗ Branch 3 not taken.
2714 , is_root_(parent == NULL && !is_nested)
53 2714 , managed_database_(false)
54 2714 , parent_(parent)
55 2714 , nested_catalog_cache_dirty_(true)
56 2714 , voms_authz_status_(kVomsUnknown)
57
1/2
✓ Branch 8 taken 2714 times.
✗ Branch 9 not taken.
5428 , initialized_(false) {
58 2714 max_row_id_ = 0;
59 2714 inode_annotation_ = NULL;
60 2714 lock_ = reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t)));
61 2714 const int retval = pthread_mutex_init(lock_, NULL);
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2714 times.
2714 assert(retval == 0);
63
64 2714 database_ = NULL;
65 2714 uid_map_ = NULL;
66 2714 gid_map_ = NULL;
67 2714 sql_listing_ = NULL;
68 2714 sql_lookup_md5path_ = NULL;
69 2714 sql_lookup_nested_ = NULL;
70 2714 sql_list_nested_ = NULL;
71 2714 sql_own_list_nested_ = NULL;
72 2714 sql_all_chunks_ = NULL;
73 2714 sql_chunks_listing_ = NULL;
74 2714 sql_lookup_xattrs_ = NULL;
75 2714 }
76
77
78 7132 Catalog::~Catalog() {
79 5406 pthread_mutex_destroy(lock_);
80 5406 free(lock_);
81 5406 FinalizePreparedStatements();
82
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
5406 delete database_;
83 7132 }
84
85
86 /**
87 * InitPreparedStatement uses polymorphism in case of a r/w catalog.
88 * FinalizePreparedStatements is called in the destructor where
89 * polymorphism does not work any more and has to be called both in
90 * the WritableCatalog and the Catalog destructor
91 */
92 2668 void Catalog::InitPreparedStatements() {
93
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_listing_ = new SqlListing(database());
94
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_lookup_md5path_ = new SqlLookupPathHash(database());
95
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_lookup_nested_ = new SqlNestedCatalogLookup(database());
96
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_list_nested_ = new SqlNestedCatalogListing(database());
97
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_own_list_nested_ = new SqlOwnNestedCatalogListing(database());
98
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_all_chunks_ = new SqlAllChunks(database());
99
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_chunks_listing_ = new SqlChunksListing(database());
100
1/2
✓ Branch 3 taken 2668 times.
✗ Branch 4 not taken.
2668 sql_lookup_xattrs_ = new SqlLookupXattrs(database());
101 2668 }
102
103
104 2703 void Catalog::FinalizePreparedStatements() {
105
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_lookup_xattrs_;
106
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_chunks_listing_;
107
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_all_chunks_;
108
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_listing_;
109
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_lookup_md5path_;
110
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_lookup_nested_;
111
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_list_nested_;
112
2/2
✓ Branch 0 taken 2659 times.
✓ Branch 1 taken 44 times.
2703 delete sql_own_list_nested_;
113 2703 }
114
115
116 348 bool Catalog::InitStandalone(const std::string &database_file) {
117
1/2
✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
348 const bool retval = OpenDatabase(database_file);
118
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 344 times.
348 if (!retval) {
119 4 return false;
120 }
121
122 344 InodeRange inode_range;
123 344 inode_range.MakeDummy();
124 344 set_inode_range(inode_range);
125 344 return true;
126 }
127
128
129 4549 bool Catalog::ReadCatalogCounters() {
130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4549 times.
4549 assert(database_ != NULL);
131 bool statistics_loaded;
132 4549 if (database().schema_version() < CatalogDatabase::kLatestSupportedSchema
133
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4537 times.
4549 - CatalogDatabase::kSchemaEpsilon) {
134 12 statistics_loaded = counters_.ReadFromDatabase(database(),
135 LegacyMode::kLegacy);
136
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4537 times.
4537 } else if (database().schema_revision() < 2) {
137 ✗ statistics_loaded = counters_.ReadFromDatabase(database(),
138 LegacyMode::kNoXattrs);
139
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4537 times.
4537 } else if (database().schema_revision() < 3) {
140 ✗ statistics_loaded = counters_.ReadFromDatabase(database(),
141 LegacyMode::kNoExternals);
142
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4537 times.
4537 } else if (database().schema_revision() < 5) {
143 ✗ statistics_loaded = counters_.ReadFromDatabase(database(),
144 LegacyMode::kNoSpecials);
145 } else {
146 4537 statistics_loaded = counters_.ReadFromDatabase(database());
147 }
148 4549 return statistics_loaded;
149 }
150
151
152 /**
153 * Establishes the database structures and opens the sqlite database file.
154 * @param db_path the absolute path to the database file on local file system
155 * @return true on successful initialization otherwise false
156 */
157 2676 bool Catalog::OpenDatabase(const string &db_path) {
158
2/4
✓ Branch 1 taken 2676 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2676 times.
✗ Branch 5 not taken.
2676 database_ = CatalogDatabase::Open(db_path, DatabaseOpenMode());
159
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2668 times.
2676 if (NULL == database_) {
160 8 return false;
161 }
162
163
2/2
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 2656 times.
2668 if (database_->IsEqualSchema(database_->schema_version(), 1.0)) {
164 // Possible fix-up for database layout lacking the content hash of
165 // nested catalogs
166 SqlCatalog sql_has_nested_sha1(
167 12 database(),
168 "SELECT count(*) FROM sqlite_master "
169
2/4
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
36 "WHERE type='table' AND name='nested_catalogs' AND sql LIKE '%sha1%';");
170
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const bool retval = sql_has_nested_sha1.FetchRow();
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 assert(retval == true);
172
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const bool has_nested_sha1 = sql_has_nested_sha1.RetrieveInt64(0);
173
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (!has_nested_sha1) {
174 6 database_->EnforceSchema(0.9, 0);
175 }
176 12 }
177
178
1/2
✓ Branch 1 taken 2668 times.
✗ Branch 2 not taken.
2668 InitPreparedStatements();
179
180 // Set the database file ownership if requested
181
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 2598 times.
2668 if (managed_database_) {
182
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 database_->TakeFileOwnership();
183 }
184
185 // Find out the maximum row id of this database file
186
2/4
✓ Branch 2 taken 2668 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2668 times.
✗ Branch 7 not taken.
5336 SqlCatalog sql_max_row_id(database(), "SELECT MAX(rowid) FROM catalog;");
187
2/4
✓ Branch 1 taken 2668 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2668 times.
2668 if (!sql_max_row_id.FetchRow()) {
188 ✗ LogCvmfs(kLogCatalog, kLogDebug,
189 "Cannot retrieve maximal row id for database file %s "
190 "(SqliteErrorcode: %d)",
191 db_path.c_str(), sql_max_row_id.GetLastError());
192 ✗ return false;
193 }
194
1/2
✓ Branch 1 taken 2668 times.
✗ Branch 2 not taken.
2668 max_row_id_ = sql_max_row_id.RetrieveInt64(0);
195
196 // Get root prefix
197
4/6
✓ Branch 2 taken 2668 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2668 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1246 times.
✓ Branch 10 taken 1422 times.
2668 if (database_->HasProperty("root_prefix")) {
198 1246 const std::string root_prefix = database_->GetProperty<std::string>(
199
2/4
✓ Branch 2 taken 1246 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1246 times.
✗ Branch 6 not taken.
2492 "root_prefix");
200
1/2
✓ Branch 3 taken 1246 times.
✗ Branch 4 not taken.
1246 root_prefix_.Assign(root_prefix.data(), root_prefix.size());
201
1/2
✓ Branch 3 taken 1246 times.
✗ Branch 4 not taken.
1246 LogCvmfs(kLogCatalog, kLogDebug,
202 "found root prefix %s in root catalog file %s",
203 root_prefix_.c_str(), db_path.c_str());
204
1/2
✓ Branch 1 taken 1246 times.
✗ Branch 2 not taken.
1246 is_regular_mountpoint_ = (root_prefix_ == mountpoint_);
205 1246 } else {
206
1/2
✓ Branch 2 taken 1422 times.
✗ Branch 3 not taken.
1422 LogCvmfs(kLogCatalog, kLogDebug, "no root prefix for root catalog file %s",
207 db_path.c_str());
208 }
209
210 // Get volatile content flag
211
2/4
✓ Branch 1 taken 2668 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2668 times.
✗ Branch 5 not taken.
2668 volatile_flag_ = database_->GetPropertyDefault<bool>("volatile",
212 2668 volatile_flag_);
213
214 // Read Catalog Counter Statistics
215
2/4
✓ Branch 1 taken 2668 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2668 times.
2668 if (!ReadCatalogCounters()) {
216 ✗ LogCvmfs(kLogCatalog, kLogStderr,
217 "failed to load statistics counters for catalog %s (file %s)",
218 mountpoint_.c_str(), db_path.c_str());
219 ✗ return false;
220 }
221
222
2/2
✓ Branch 1 taken 1122 times.
✓ Branch 2 taken 1546 times.
2668 if (HasParent()) {
223
1/2
✓ Branch 1 taken 1122 times.
✗ Branch 2 not taken.
1122 parent_->AddChild(this);
224 }
225
226 2668 initialized_ = true;
227 2668 return true;
228 2668 }
229
230
231 /**
232 * Removes the mountpoint and prepends the root prefix to path
233 */
234 16244 shash::Md5 Catalog::NormalizePath(const PathString &path) const {
235
2/2
✓ Branch 0 taken 16172 times.
✓ Branch 1 taken 72 times.
16244 if (is_regular_mountpoint_)
236
1/2
✓ Branch 3 taken 16172 times.
✗ Branch 4 not taken.
16172 return shash::Md5(path.GetChars(), path.GetLength());
237
238
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
72 assert(path.GetLength() >= mountpoint_.GetLength());
239 // Piecewise hash calculation: root_prefix plus tail of path
240
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 shash::Any result(shash::kMd5);
241
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 shash::ContextPtr ctx(shash::kMd5);
242 72 ctx.buffer = alloca(ctx.size);
243
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 shash::Init(ctx);
244
1/2
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
144 shash::Update(
245 72 reinterpret_cast<const unsigned char *>(root_prefix_.GetChars()),
246 root_prefix_.GetLength(),
247 ctx);
248 72 shash::Update(reinterpret_cast<const unsigned char *>(path.GetChars())
249
1/2
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
72 + mountpoint_.GetLength(),
250 72 path.GetLength() - mountpoint_.GetLength(),
251 ctx);
252
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 shash::Final(ctx, &result);
253
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 return result.CastToMd5();
254 }
255
256
257 /**
258 * Same as NormalizePath but returns a PathString instead of an Md5 hash.
259 */
260 1185 PathString Catalog::NormalizePath2(const PathString &path) const {
261
2/2
✓ Branch 0 taken 1161 times.
✓ Branch 1 taken 24 times.
1185 if (is_regular_mountpoint_)
262
1/2
✓ Branch 1 taken 1161 times.
✗ Branch 2 not taken.
1161 return path;
263
264
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
24 assert(path.GetLength() >= mountpoint_.GetLength());
265
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 PathString result = root_prefix_;
266
1/2
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 const PathString suffix = path.Suffix(mountpoint_.GetLength());
267
1/2
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
24 result.Append(suffix.GetChars(), suffix.GetLength());
268
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 return result;
269 24 }
270
271
272 /**
273 * The opposite of NormalizePath: from a full path remove the root prefix and
274 * add the catalogs current mountpoint. Needed for normalized paths from the
275 * SQlite tables, such as nested catalog entry points.
276 */
277 1327 PathString Catalog::PlantPath(const PathString &path) const {
278
2/2
✓ Branch 0 taken 1303 times.
✓ Branch 1 taken 24 times.
1327 if (is_regular_mountpoint_)
279
1/2
✓ Branch 1 taken 1303 times.
✗ Branch 2 not taken.
1303 return path;
280
281
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
24 assert(path.GetLength() >= root_prefix_.GetLength());
282
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 PathString result = mountpoint_;
283
1/2
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 const PathString suffix = path.Suffix(root_prefix_.GetLength());
284
1/2
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
24 result.Append(suffix.GetChars(), suffix.GetLength());
285
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 return result;
286 24 }
287
288
289 /**
290 * Performs a lookup on this Catalog for a given MD5 path hash.
291 * @param md5path the MD5 hash of the searched path
292 * @param expand_symlink indicates if variables in symlink should be resolved
293 * @param dirent will be set to the found DirectoryEntry
294 * @return true if DirectoryEntry was successfully found, false otherwise
295 */
296 14679 bool Catalog::LookupEntry(const shash::Md5 &md5path, const bool expand_symlink,
297 DirectoryEntry *dirent) const {
298
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14679 times.
14679 assert(IsInitialized());
299
300 14679 const MutexLockGuard m(lock_);
301
1/2
✓ Branch 1 taken 14679 times.
✗ Branch 2 not taken.
14679 sql_lookup_md5path_->BindPathHash(md5path);
302
1/2
✓ Branch 1 taken 14679 times.
✗ Branch 2 not taken.
14679 const bool found = sql_lookup_md5path_->FetchRow();
303
4/4
✓ Branch 0 taken 14122 times.
✓ Branch 1 taken 557 times.
✓ Branch 2 taken 14109 times.
✓ Branch 3 taken 13 times.
14679 if (found && (dirent != NULL)) {
304
2/4
✓ Branch 1 taken 14109 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14109 times.
✗ Branch 5 not taken.
14109 *dirent = sql_lookup_md5path_->GetDirent(this, expand_symlink);
305
1/2
✓ Branch 1 taken 14109 times.
✗ Branch 2 not taken.
14109 FixTransitionPoint(md5path, dirent);
306 }
307
1/2
✓ Branch 1 taken 14679 times.
✗ Branch 2 not taken.
14679 sql_lookup_md5path_->Reset();
308
309 14679 return found;
310 14679 }
311
312
313 /**
314 * Performs a lookup on this Catalog for a given MD5 path hash.
315 * @param md5path the MD5 hash of the searched path
316 * @param dirent will be set to the found DirectoryEntry
317 * @return true if DirectoryEntry was successfully found, false otherwise
318 */
319 14675 bool Catalog::LookupMd5Path(const shash::Md5 &md5path,
320 DirectoryEntry *dirent) const {
321 14675 return LookupEntry(md5path, true, dirent);
322 }
323
324
325 4 bool Catalog::LookupRawSymlink(const PathString &path,
326 LinkString *raw_symlink) const {
327
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 DirectoryEntry dirent;
328
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 const bool result = (LookupEntry(NormalizePath(path), false, &dirent));
329
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (result)
330
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 raw_symlink->Assign(dirent.symlink());
331 4 return result;
332 4 }
333
334
335 4 bool Catalog::LookupXattrsMd5Path(const shash::Md5 &md5path,
336 XattrList *xattrs) const {
337
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 assert(IsInitialized());
338
339 4 const MutexLockGuard m(lock_);
340
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 sql_lookup_xattrs_->BindPathHash(md5path);
341
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 const bool found = sql_lookup_xattrs_->FetchRow();
342
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if (found && (xattrs != NULL)) {
343
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 *xattrs = sql_lookup_xattrs_->GetXattrs();
344 }
345
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 sql_lookup_xattrs_->Reset();
346
347 4 return found;
348 4 }
349
350
351 /**
352 * Perform a listing of the directory with the given MD5 path hash.
353 * @param path_hash the MD5 hash of the path of the directory to list
354 * @param listing will be set to the resulting DirectoryEntryList
355 * @return true on successful listing, false otherwise
356 */
357 110 bool Catalog::ListingMd5PathStat(const shash::Md5 &md5path,
358 StatEntryList *listing) const {
359
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
110 assert(IsInitialized());
360
361
1/2
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
110 DirectoryEntry dirent;
362
1/2
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
110 StatEntry entry;
363
364 110 const MutexLockGuard m(lock_);
365
1/2
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
110 sql_listing_->BindPathHash(md5path);
366
3/4
✓ Branch 1 taken 428 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 318 times.
✓ Branch 4 taken 110 times.
428 while (sql_listing_->FetchRow()) {
367
2/4
✓ Branch 1 taken 318 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 318 times.
✗ Branch 5 not taken.
318 dirent = sql_listing_->GetDirent(this);
368
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 314 times.
318 if (dirent.IsHidden())
369 4 continue;
370
1/2
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
314 FixTransitionPoint(md5path, &dirent);
371
2/4
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 314 times.
✗ Branch 5 not taken.
314 entry.name = dirent.name();
372
1/2
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
314 entry.info = dirent.GetStatStructure();
373
1/2
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
314 listing->PushBack(entry);
374 }
375
1/2
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
110 sql_listing_->Reset();
376
377 110 return true;
378 110 }
379
380
381 /**
382 * Perform a listing of the directory with the given MD5 path hash.
383 * Returns only struct stat values
384 * @param path_hash the MD5 hash of the path of the directory to list
385 * @param listing will be set to the resulting DirectoryEntryList
386 * @param expand_symlink defines if magic symlinks should be resolved
387 * @return true on successful listing, false otherwise
388 */
389 1781 bool Catalog::ListingMd5Path(const shash::Md5 &md5path,
390 DirectoryEntryList *listing,
391 const bool expand_symlink) const {
392
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1781 times.
1781 assert(IsInitialized());
393
394 1781 const MutexLockGuard m(lock_);
395
396
1/2
✓ Branch 1 taken 1781 times.
✗ Branch 2 not taken.
1781 sql_listing_->BindPathHash(md5path);
397
3/4
✓ Branch 1 taken 5259 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3478 times.
✓ Branch 4 taken 1781 times.
5259 while (sql_listing_->FetchRow()) {
398
1/2
✓ Branch 1 taken 3478 times.
✗ Branch 2 not taken.
3478 DirectoryEntry dirent = sql_listing_->GetDirent(this, expand_symlink);
399
1/2
✓ Branch 1 taken 3478 times.
✗ Branch 2 not taken.
3478 FixTransitionPoint(md5path, &dirent);
400
1/2
✓ Branch 1 taken 3478 times.
✗ Branch 2 not taken.
3478 listing->push_back(dirent);
401 3478 }
402
1/2
✓ Branch 1 taken 1781 times.
✗ Branch 2 not taken.
1781 sql_listing_->Reset();
403
404 1781 return true;
405 1781 }
406
407
408 10 bool Catalog::AllChunksBegin() { return sql_all_chunks_->Open(); }
409
410
411 52 bool Catalog::AllChunksNext(shash::Any *hash,
412 zlib::Algorithms *compression_alg) {
413 52 return sql_all_chunks_->Next(hash, compression_alg);
414 }
415
416
417 10 bool Catalog::AllChunksEnd() { return sql_all_chunks_->Close(); }
418
419
420 /**
421 * Hash algorithm is given by the unchunked file.
422 * Could be figured out by a join but it is faster if the user of this
423 * method tells us.
424 */
425 10 bool Catalog::ListMd5PathChunks(const shash::Md5 &md5path,
426 const shash::Algorithms interpret_hashes_as,
427 FileChunkList *chunks) const {
428
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 assert(IsInitialized() && chunks->IsEmpty());
429
430 10 const MutexLockGuard m(lock_);
431
432
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 sql_chunks_listing_->BindPathHash(md5path);
433
3/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 10 times.
20 while (sql_chunks_listing_->FetchRow()) {
434
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 chunks->PushBack(sql_chunks_listing_->GetFileChunk(interpret_hashes_as));
435 }
436
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 sql_chunks_listing_->Reset();
437
438 10 return true;
439 10 }
440
441
442 /**
443 * Only used by the garbage collection
444 */
445 10 const Catalog::HashVector &Catalog::GetReferencedObjects() const {
446
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (!referenced_hashes_.empty()) {
447 ✗ return referenced_hashes_;
448 }
449
450 // retrieve all referenced content hashes of both files and file chunks
451
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 SqlListContentHashes list_content_hashes(database());
452
3/4
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
✓ Branch 4 taken 10 times.
72 while (list_content_hashes.FetchRow()) {
453
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
62 referenced_hashes_.push_back(list_content_hashes.GetHash());
454 }
455
456 10 return referenced_hashes_;
457 10 }
458
459
460 1919 void Catalog::TakeDatabaseFileOwnership() {
461 1919 managed_database_ = true;
462
2/2
✓ Branch 0 taken 1849 times.
✓ Branch 1 taken 70 times.
1919 if (NULL != database_) {
463 1849 database_->TakeFileOwnership();
464 }
465 1919 }
466
467
468 4 void Catalog::DropDatabaseFileOwnership() {
469 4 managed_database_ = false;
470
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (NULL != database_) {
471 4 database_->DropFileOwnership();
472 }
473 4 }
474
475
476 544 uint64_t Catalog::GetTTL() const {
477 544 const MutexLockGuard m(lock_);
478
2/4
✓ Branch 3 taken 544 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 544 times.
✗ Branch 7 not taken.
1088 return database().GetPropertyDefault<uint64_t>("TTL", kDefaultTTL);
479 544 }
480
481
482 ✗ bool Catalog::HasExplicitTTL() const {
483 ✗ const MutexLockGuard m(lock_);
484 ✗ return database().HasProperty("TTL");
485 }
486
487
488 1266 bool Catalog::GetVOMSAuthz(string *authz) const {
489 bool result;
490 1266 const MutexLockGuard m(lock_);
491
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1266 times.
1266 if (voms_authz_status_ == kVomsPresent) {
492 ✗ if (authz) {
493 ✗ *authz = voms_authz_;
494 }
495 ✗ result = true;
496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1266 times.
1266 } else if (voms_authz_status_ == kVomsNone) {
497 ✗ result = false;
498 } else {
499
3/6
✓ Branch 3 taken 1266 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1266 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1266 times.
1266 if (database().HasProperty("voms_authz")) {
500 ✗ voms_authz_ = database().GetProperty<string>("voms_authz");
501 ✗ if (authz) {
502 ✗ *authz = voms_authz_;
503 }
504 ✗ voms_authz_status_ = kVomsPresent;
505 } else {
506 1266 voms_authz_status_ = kVomsNone;
507 }
508 1266 result = (voms_authz_status_ == kVomsPresent);
509 }
510 1266 return result;
511 1266 }
512
513 3033 uint64_t Catalog::GetRevision() const {
514 3033 const MutexLockGuard m(lock_);
515
2/4
✓ Branch 3 taken 3033 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3033 times.
✗ Branch 7 not taken.
6066 return database().GetPropertyDefault<uint64_t>("revision", 0);
516 3033 }
517
518 1276 uint64_t Catalog::GetLastModified() const {
519
1/2
✓ Branch 2 taken 1276 times.
✗ Branch 3 not taken.
1276 const std::string prop_name = "last_modified";
520
1/2
✓ Branch 2 taken 1276 times.
✗ Branch 3 not taken.
1276 return (database().HasProperty(prop_name))
521
2/4
✓ Branch 0 taken 1276 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 1276 times.
✗ Branch 5 not taken.
1276 ? database().GetProperty<int>(prop_name)
522 1276 : 0u;
523 1276 }
524
525
526 ✗ uint64_t Catalog::GetNumChunks() const {
527 ✗ return counters_.Get("self_regular") + counters_.Get("self_chunks");
528 }
529
530
531 8 uint64_t Catalog::GetNumEntries() const {
532
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 const string sql = "SELECT count(*) FROM catalog;";
533
534 8 const MutexLockGuard m(lock_);
535
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 SqlCatalog stmt(database(), sql);
536
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
16 return (stmt.FetchRow()) ? stmt.RetrieveInt64(0) : 0;
537 8 }
538
539
540 10 shash::Any Catalog::GetPreviousRevision() const {
541 10 const MutexLockGuard m(lock_);
542 10 const std::string hash_string = database().GetPropertyDefault<std::string>(
543
3/6
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 10 times.
✗ Branch 10 not taken.
20 "previous_revision", "");
544 10 return (!hash_string.empty())
545
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 ? shash::MkFromHexPtr(shash::HexPtr(hash_string),
546 shash::kSuffixCatalog)
547
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
20 : shash::Any();
548 10 }
549
550
551 6 string Catalog::PrintMemStatistics() const {
552 6 sqlite::MemStatistics stats;
553 {
554 6 const MutexLockGuard m(lock_);
555
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 database().GetMemStatistics(&stats);
556 6 }
557
4/8
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6 times.
✗ Branch 14 not taken.
12 return string(mountpoint().GetChars(), mountpoint().GetLength()) + ": "
558
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.lookaside_slots_used) + " / "
559
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.lookaside_slots_max) + " slots -- "
560
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.lookaside_hit) + " hits, "
561
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.lookaside_miss_size) + " misses-size, "
562
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.lookaside_miss_full) + " misses-full -- "
563
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.page_cache_used / 1024) + " kB pages -- "
564
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.page_cache_hit) + " hits, "
565
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.page_cache_miss) + " misses -- "
566
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.schema_used / 1024) + " kB schema -- "
567
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
24 + StringifyInt(stats.stmt_used / 1024) + " kB statements";
568 }
569
570
571 /**
572 * Determine the actual inode of a DirectoryEntry.
573 * The first used entry from a hardlink group deterimines the inode of the
574 * others.
575 * @param row_id the row id of a read row in the sqlite database
576 * @param hardlink_group the id of a possibly present hardlink group
577 * @return the assigned inode number
578 */
579 17905 inode_t Catalog::GetMangledInode(const uint64_t row_id,
580 const uint64_t hardlink_group) const {
581
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17905 times.
17905 assert(IsInitialized());
582
583
2/2
✓ Branch 1 taken 180 times.
✓ Branch 2 taken 17725 times.
17905 if (inode_range_.IsDummy()) {
584 180 return DirectoryEntry::kInvalidInode;
585 }
586
587 17725 inode_t inode = row_id + inode_range_.offset;
588
589 // Hardlinks are encoded in catalog-wide unique hard link group ids.
590 // These ids must be resolved to actual inode relationships at runtime.
591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17725 times.
17725 if (hardlink_group > 0) {
592 ✗ const HardlinkGroupMap::const_iterator inode_iter = hardlink_groups_.find(
593 ✗ hardlink_group);
594
595 // Use cached entry if possible
596 ✗ if (inode_iter == hardlink_groups_.end()) {
597 ✗ hardlink_groups_[hardlink_group] = inode;
598 } else {
599 ✗ inode = inode_iter->second;
600 }
601 }
602
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17725 times.
17725 if (inode_annotation_) {
604 ✗ inode = inode_annotation_->Annotate(inode);
605 }
606
607 17725 return inode;
608 }
609
610
611 /**
612 * Get a list of all registered nested catalogs and bind mountpoints in this
613 * catalog.
614 * @return a list of all nested catalog and bind mountpoints.
615 */
616 10287 const Catalog::NestedCatalogList &Catalog::ListNestedCatalogs() const {
617 10287 const MutexLockGuard m(lock_);
618
619
2/2
✓ Branch 0 taken 2133 times.
✓ Branch 1 taken 8154 times.
10287 if (nested_catalog_cache_dirty_) {
620
1/2
✓ Branch 2 taken 2133 times.
✗ Branch 3 not taken.
2133 LogCvmfs(kLogCatalog, kLogDebug, "refreshing nested catalog cache of '%s'",
621
1/2
✓ Branch 1 taken 2133 times.
✗ Branch 2 not taken.
4266 mountpoint().c_str());
622
3/4
✓ Branch 1 taken 3226 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1093 times.
✓ Branch 4 taken 2133 times.
3226 while (sql_list_nested_->FetchRow()) {
623
1/2
✓ Branch 1 taken 1093 times.
✗ Branch 2 not taken.
1093 NestedCatalog nested;
624
3/6
✓ Branch 1 taken 1093 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1093 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1093 times.
✗ Branch 8 not taken.
1093 nested.mountpoint = PlantPath(sql_list_nested_->GetPath());
625
1/2
✓ Branch 1 taken 1093 times.
✗ Branch 2 not taken.
1093 nested.hash = sql_list_nested_->GetContentHash();
626
1/2
✓ Branch 1 taken 1093 times.
✗ Branch 2 not taken.
1093 nested.size = sql_list_nested_->GetSize();
627
1/2
✓ Branch 1 taken 1093 times.
✗ Branch 2 not taken.
1093 nested_catalog_cache_.push_back(nested);
628 1093 }
629
1/2
✓ Branch 1 taken 2133 times.
✗ Branch 2 not taken.
2133 sql_list_nested_->Reset();
630 2133 nested_catalog_cache_dirty_ = false;
631 }
632
633 10287 return nested_catalog_cache_;
634 10287 }
635
636
637 /**
638 * Get a list of all registered nested catalogs without bind mountpoints. Used
639 * for replication and garbage collection.
640 * @return a list of all nested catalogs.
641 */
642 836 const Catalog::NestedCatalogList Catalog::ListOwnNestedCatalogs() const {
643 836 NestedCatalogList result;
644
645 836 const MutexLockGuard m(lock_);
646
647
3/4
✓ Branch 1 taken 1034 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 198 times.
✓ Branch 4 taken 836 times.
1034 while (sql_own_list_nested_->FetchRow()) {
648
1/2
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
198 NestedCatalog nested;
649
3/6
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 198 times.
✗ Branch 8 not taken.
198 nested.mountpoint = PlantPath(sql_own_list_nested_->GetPath());
650
1/2
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
198 nested.hash = sql_own_list_nested_->GetContentHash();
651
1/2
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
198 nested.size = sql_own_list_nested_->GetSize();
652
1/2
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
198 result.push_back(nested);
653 198 }
654
1/2
✓ Branch 1 taken 836 times.
✗ Branch 2 not taken.
836 sql_own_list_nested_->Reset();
655
656 1672 return result;
657 836 }
658
659
660 /**
661 * Drops the nested catalog cache. Usually this is only useful in subclasses
662 * that implement writable catalogs.
663 *
664 * Note: this action is _not_ secured by the catalog's mutex. If serialisation
665 * is required the subclass needs to ensure that.
666 */
667 1528 void Catalog::ResetNestedCatalogCacheUnprotected() {
668 1528 nested_catalog_cache_.clear();
669 1528 nested_catalog_cache_dirty_ = true;
670 1528 }
671
672
673 /**
674 * Looks for a specific registered nested catalog based on a path.
675 */
676 1149 bool Catalog::FindNested(const PathString &mountpoint, shash::Any *hash,
677 uint64_t *size) const {
678 1149 const MutexLockGuard m(lock_);
679
1/2
✓ Branch 1 taken 1149 times.
✗ Branch 2 not taken.
1149 const PathString normalized_mountpoint = NormalizePath2(mountpoint);
680
1/2
✓ Branch 1 taken 1149 times.
✗ Branch 2 not taken.
1149 sql_lookup_nested_->BindSearchPath(normalized_mountpoint);
681
1/2
✓ Branch 1 taken 1149 times.
✗ Branch 2 not taken.
1149 const bool found = sql_lookup_nested_->FetchRow();
682
3/4
✓ Branch 0 taken 1130 times.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 1130 times.
✗ Branch 3 not taken.
1149 if (found && (hash != NULL)) {
683
1/2
✓ Branch 1 taken 1130 times.
✗ Branch 2 not taken.
1130 *hash = sql_lookup_nested_->GetContentHash();
684
1/2
✓ Branch 1 taken 1130 times.
✗ Branch 2 not taken.
1130 *size = sql_lookup_nested_->GetSize();
685 }
686
1/2
✓ Branch 1 taken 1149 times.
✗ Branch 2 not taken.
1149 sql_lookup_nested_->Reset();
687
688 1149 return found;
689 1149 }
690
691
692 /**
693 * Sets a new object to do inode annotations (or set to NULL)
694 * The annotation object is not owned by the catalog.
695 */
696 2320 void Catalog::SetInodeAnnotation(InodeAnnotation *new_annotation) {
697 2320 const MutexLockGuard m(lock_);
698 // Since annotated inodes could come back to the catalog in order to
699 // get stripped, exchanging the annotation is not allowed
700
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2320 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2320 assert((inode_annotation_ == NULL) || (inode_annotation_ == new_annotation));
701 2320 inode_annotation_ = new_annotation;
702 2320 }
703
704
705 2320 void Catalog::SetOwnerMaps(const OwnerMap *uid_map, const OwnerMap *gid_map) {
706
2/4
✓ Branch 0 taken 2320 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2320 times.
2320 uid_map_ = (uid_map && uid_map->HasEffect()) ? uid_map : NULL;
707
2/4
✓ Branch 0 taken 2320 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2320 times.
2320 gid_map_ = (gid_map && gid_map->HasEffect()) ? gid_map : NULL;
708 2320 }
709
710
711 /**
712 * Add a Catalog as child to this Catalog.
713 * @param child the Catalog to define as child
714 */
715 1122 void Catalog::AddChild(Catalog *child) {
716
3/7
✓ Branch 1 taken 1122 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1122 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1122 times.
1122 assert(NULL == FindChild(child->mountpoint()));
717
718 1122 const MutexLockGuard m(lock_);
719
2/4
✓ Branch 1 taken 1122 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1122 times.
✗ Branch 5 not taken.
1122 children_[child->mountpoint()] = child;
720 1122 child->set_parent(this);
721 1122 }
722
723
724 /**
725 * Removes a Catalog from the children list of this Catalog
726 * @param child the Catalog to delete as child
727 */
728 1058 void Catalog::RemoveChild(Catalog *child) {
729
3/7
✓ Branch 1 taken 1058 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1058 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1058 times.
1058 assert(NULL != FindChild(child->mountpoint()));
730
731 1058 const MutexLockGuard m(lock_);
732 1058 child->set_parent(NULL);
733
2/4
✓ Branch 1 taken 1058 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1058 times.
✗ Branch 5 not taken.
1058 children_.erase(child->mountpoint());
734 1058 }
735
736
737 3552 CatalogList Catalog::GetChildren() const {
738 3552 CatalogList result;
739
740 3552 const MutexLockGuard m(lock_);
741 7104 for (NestedCatalogMap::const_iterator i = children_.begin(),
742 3552 iEnd = children_.end();
743
2/2
✓ Branch 1 taken 1671 times.
✓ Branch 2 taken 3552 times.
5223 i != iEnd;
744 1671 ++i) {
745
1/2
✓ Branch 2 taken 1671 times.
✗ Branch 3 not taken.
1671 result.push_back(i->second);
746 }
747
748 7104 return result;
749 3552 }
750
751
752 /**
753 * Find the nested catalog that serves the given path.
754 * It might be possible that the path is in fact served by a child of the found
755 * nested catalog.
756 * @param path the path to find a best fitting catalog for
757 * @return a pointer to the best fitting child or NULL if it does not fit at all
758 */
759 11669 Catalog *Catalog::FindSubtree(const PathString &path) const {
760 // Check if this catalog fits the beginning of the path.
761
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11669 times.
11669 if (!path.StartsWith(mountpoint_))
762 ✗ return NULL;
763
764
1/2
✓ Branch 2 taken 11669 times.
✗ Branch 3 not taken.
11669 PathString remaining(path.Suffix(mountpoint_.GetLength()));
765
1/2
✓ Branch 1 taken 11669 times.
✗ Branch 2 not taken.
11669 remaining.Append("/", 1);
766
767 // now we recombine the path elements successively
768 // in order to find a child which serves a part of the path
769
1/2
✓ Branch 1 taken 11669 times.
✗ Branch 2 not taken.
11669 PathString path_prefix(mountpoint_);
770 11669 Catalog *result = NULL;
771 // Skip the first '/'
772
1/2
✓ Branch 1 taken 11669 times.
✗ Branch 2 not taken.
11669 path_prefix.Append("/", 1);
773 11669 const char *c = remaining.GetChars() + 1;
774
2/2
✓ Branch 1 taken 103174 times.
✓ Branch 2 taken 9720 times.
112894 for (unsigned i = 1; i < remaining.GetLength(); ++i, ++c) {
775
2/2
✓ Branch 0 taken 23079 times.
✓ Branch 1 taken 80095 times.
103174 if (*c == '/') {
776
1/2
✓ Branch 1 taken 23079 times.
✗ Branch 2 not taken.
23079 result = FindChild(path_prefix);
777
778 // If we found a child serving a part of the path we can stop searching.
779 // Remaining sub path elements are possibly served by a grand child.
780
2/2
✓ Branch 0 taken 1949 times.
✓ Branch 1 taken 21130 times.
23079 if (result != NULL)
781 1949 break;
782 }
783
1/2
✓ Branch 1 taken 101225 times.
✗ Branch 2 not taken.
101225 path_prefix.Append(c, 1);
784 }
785
786 11669 return result;
787 11669 }
788
789
790 /**
791 * Looks for a child catalog, which is a subset of all registered nested
792 * catalogs.
793 */
794 25408 Catalog *Catalog::FindChild(const PathString &mountpoint) const {
795 25408 NestedCatalogMap::const_iterator nested_iter;
796
797 25408 const MutexLockGuard m(lock_);
798
1/2
✓ Branch 1 taken 25408 times.
✗ Branch 2 not taken.
25408 nested_iter = children_.find(mountpoint);
799
2/2
✓ Branch 2 taken 22350 times.
✓ Branch 3 taken 3058 times.
28466 Catalog *result = (nested_iter == children_.end()) ? NULL
800 3058 : nested_iter->second;
801
802 25408 return result;
803 25408 }
804
805
806 /**
807 * For the transition points for nested catalogs and bind mountpoints, the inode
808 * is ambiguous. It has to be set to the parent inode because nested catalogs
809 * are lazily loaded.
810 * @param md5path the MD5 hash of the entry to check
811 * @param dirent the DirectoryEntry to perform coherence fixes on
812 */
813 17901 void Catalog::FixTransitionPoint(const shash::Md5 &md5path,
814 DirectoryEntry *dirent) const {
815
2/2
✓ Branch 1 taken 13424 times.
✓ Branch 2 taken 4477 times.
17901 if (!HasParent())
816 13424 return;
817
818
2/2
✓ Branch 1 taken 334 times.
✓ Branch 2 taken 4143 times.
4477 if (dirent->IsNestedCatalogRoot()) {
819 // Normal nested catalog
820
1/2
✓ Branch 1 taken 334 times.
✗ Branch 2 not taken.
334 DirectoryEntry parent_dirent;
821
1/2
✓ Branch 1 taken 334 times.
✗ Branch 2 not taken.
334 const bool retval = parent_->LookupMd5Path(md5path, &parent_dirent);
822
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 334 times.
334 assert(retval);
823 334 dirent->set_inode(parent_dirent.inode());
824
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4143 times.
4477 } else if (md5path == kMd5PathEmpty) {
825 // Bind mountpoint
826 ✗ DirectoryEntry parent_dirent;
827 ✗ const bool retval = parent_->LookupPath(mountpoint_, &parent_dirent);
828 ✗ assert(retval);
829 ✗ dirent->set_inode(parent_dirent.inode());
830 }
831 }
832
833 } // namespace catalog
834