GCC Code Coverage Report


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