GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_mgr_impl.h
Date: 2025-03-09 02:34:28
Exec Total Coverage
Lines: 419 566 74.0%
Branches: 295 654 45.1%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System
3 */
4
5 // avoid clang-tidy false positives (at least starting with clang14)
6 //NOLINTBEGIN
7
8 #ifndef CVMFS_CATALOG_MGR_IMPL_H_
9 #define CVMFS_CATALOG_MGR_IMPL_H_
10
11 #ifndef __STDC_FORMAT_MACROS
12 #define __STDC_FORMAT_MACROS
13 #endif
14
15
16
17 #include <cassert>
18 #include <string>
19 #include <vector>
20
21 #include "shortstring.h"
22 #include "statistics.h"
23 #include "util/logging.h"
24 #include "xattr.h"
25
26 using namespace std; // NOLINT
27
28 namespace catalog {
29
30 template <class CatalogT>
31 75 AbstractCatalogManager<CatalogT>::AbstractCatalogManager(
32 perf::Statistics *statistics) :
33
1/2
✓ Branch 4 taken 75 times.
✗ Branch 5 not taken.
75 statistics_(statistics) {
34 75 inode_watermark_status_ = 0;
35 75 inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset;
36 75 revision_cache_ = 0;
37 75 timestamp_cache_ = 0;
38 75 catalog_watermark_ = 0;
39 75 volatile_flag_ = false;
40 75 has_authz_cache_ = false;
41 75 inode_annotation_ = NULL;
42 75 incarnation_ = 0;
43 75 rwlock_ =
44 75 reinterpret_cast<pthread_rwlock_t *>(smalloc(sizeof(pthread_rwlock_t)));
45 75 int retval = pthread_rwlock_init(rwlock_, NULL);
46
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 assert(retval == 0);
47 75 retval = pthread_key_create(&pkey_sqlitemem_, NULL);
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 assert(retval == 0);
49 75 }
50
51 template <class CatalogT>
52 150 AbstractCatalogManager<CatalogT>::~AbstractCatalogManager() {
53 150 DetachAll();
54 150 pthread_key_delete(pkey_sqlitemem_);
55 150 pthread_rwlock_destroy(rwlock_);
56 150 free(rwlock_);
57 }
58
59 template <class CatalogT>
60 24 void AbstractCatalogManager<CatalogT>::SetInodeAnnotation(
61 InodeAnnotation *new_annotation)
62 {
63
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
24 assert(catalogs_.empty() || (new_annotation == inode_annotation_));
64 24 inode_annotation_ = new_annotation;
65 24 }
66
67 template <class CatalogT>
68 31 void AbstractCatalogManager<CatalogT>::SetOwnerMaps(const OwnerMap &uid_map,
69 const OwnerMap &gid_map)
70 {
71 31 uid_map_ = uid_map;
72 31 gid_map_ = gid_map;
73 31 }
74
75 template <class CatalogT>
76 24 void AbstractCatalogManager<CatalogT>::SetCatalogWatermark(unsigned limit) {
77 24 catalog_watermark_ = limit;
78 24 }
79
80 template <class CatalogT>
81 133 void AbstractCatalogManager<CatalogT>::CheckInodeWatermark() {
82
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
133 if (inode_watermark_status_ > 0)
83 return;
84
85 133 uint64_t highest_inode = inode_gauge_;
86
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 115 times.
133 if (inode_annotation_)
87 18 highest_inode += inode_annotation_->GetGeneration();
88 133 uint64_t uint32_border = 1;
89 133 uint32_border = uint32_border << 32;
90
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
133 if (highest_inode >= uint32_border) {
91 LogCvmfs(kLogCatalog, kLogDebug | kLogSyslogWarn, "inodes exceed 32bit");
92 inode_watermark_status_++;
93 }
94 }
95
96
97 /**
98 * Initializes the CatalogManager and loads and attaches the root entry.
99 * @return true on successful init, otherwise false
100 */
101 template <class CatalogT>
102 57 bool AbstractCatalogManager<CatalogT>::Init() {
103 57 LogCvmfs(kLogCatalog, kLogDebug, "Initialize catalog");
104 57 WriteLock();
105
2/4
✓ Branch 2 taken 57 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 57 times.
✗ Branch 6 not taken.
57 bool attached = MountCatalog(PathString("", 0), shash::Any(), NULL);
106 57 Unlock();
107
108
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 52 times.
57 if (!attached) {
109 5 LogCvmfs(kLogCatalog, kLogDebug, "failed to initialize root catalog");
110 }
111
112 57 return attached;
113 }
114
115
116 /**
117 * Remounts the root catalog if necessary. If a newer root catalog exists,
118 * it is mounted and replaces the currently mounted tree (all existing catalogs
119 * are detached)
120 */
121 template <class CatalogT>
122 3 LoadReturn AbstractCatalogManager<CatalogT>::RemountDryrun() {
123
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 LogCvmfs(kLogCatalog, kLogDebug,
124 "dryrun remounting repositories");
125
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 CatalogContext ctlg_context;
126
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 return GetNewRootCatalogContext(&ctlg_context);
127 3 }
128
129 template <class CatalogT>
130 3 LoadReturn AbstractCatalogManager<CatalogT>::Remount() {
131
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 LogCvmfs(kLogCatalog, kLogDebug, "remounting repositories");
132
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 CatalogContext ctlg_context;
133
134
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (GetNewRootCatalogContext(&ctlg_context) != kLoadNew
135
5/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
3 && GetNewRootCatalogContext(&ctlg_context) != kLoadUp2Date) {
136 LogCvmfs(kLogCatalog, kLogDebug, "remounting repositories: "
137 "Did not find any valid root catalog to mount");
138 return kLoadFail;
139 }
140
141 3 WriteLock();
142
143
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const LoadReturn load_error = LoadCatalogByHash(&ctlg_context);
144
145
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (load_error == kLoadNew) {
146 2 inode_t old_inode_gauge = inode_gauge_;
147
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 DetachAll();
148 2 inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset;
149
150
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 CatalogT *new_root = CreateCatalog(ctlg_context.mountpoint(),
151 2 ctlg_context.hash(), NULL);
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(new_root);
153
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 bool retval = AttachCatalog(ctlg_context.sqlite_path(), new_root);
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(retval);
155
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (inode_annotation_) {
157 inode_annotation_->IncGeneration(old_inode_gauge);
158 }
159 }
160
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 CheckInodeWatermark();
161 3 Unlock();
162
163 3 return load_error;
164 3 }
165
166 /**
167 * Remounts to the given hash
168 */
169 template <class CatalogT>
170 LoadReturn AbstractCatalogManager<CatalogT>::ChangeRoot(
171 const shash::Any &root_hash)
172 {
173 assert(!root_hash.IsNull());
174 LogCvmfs(kLogCatalog, kLogDebug,
175 "switching to root hash %s", root_hash.ToString().c_str());
176
177 WriteLock();
178
179 CatalogContext ctlg_context(root_hash, PathString("", 0),
180 kCtlgNoLocationNeeded);
181 // we do not need to set revision as LoadCatalogByHash
182 // needs only mountpoint, hash
183
184 const LoadReturn load_error = LoadCatalogByHash(&ctlg_context);
185
186 if (load_error == kLoadNew) {
187 inode_t old_inode_gauge = inode_gauge_;
188 DetachAll();
189 inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset;
190
191 CatalogT *new_root =
192 CreateCatalog(PathString("", 0), ctlg_context.hash(), NULL);
193 assert(new_root);
194 bool retval = AttachCatalog(ctlg_context.sqlite_path(), new_root);
195 assert(retval);
196
197 if (inode_annotation_) {
198 inode_annotation_->IncGeneration(old_inode_gauge);
199 }
200 }
201 CheckInodeWatermark();
202 Unlock();
203
204 return load_error;
205 }
206
207
208 /**
209 * Detaches everything except the root catalog
210 */
211 template <class CatalogT>
212 5 void AbstractCatalogManager<CatalogT>::DetachNested() {
213 5 WriteLock();
214
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (catalogs_.empty()) {
215 Unlock();
216 return;
217 }
218
219 5 typename CatalogList::const_iterator i;
220 5 typename CatalogList::const_iterator iend;
221
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 CatalogList catalogs_to_detach = GetRootCatalog()->GetChildren();
222 5 for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end();
223
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 5 times.
9 i != iend; ++i)
224 {
225
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 DetachSubtree(*i);
226 }
227
228 5 Unlock();
229 5 }
230
231
232 /**
233 * Returns the NULL hash if the nested catalog is not found.
234 */
235 template <class CatalogT>
236 8 shash::Any AbstractCatalogManager<CatalogT>::GetNestedCatalogHash(
237 const PathString &mountpoint)
238 {
239
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 assert(!mountpoint.IsEmpty());
240
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 CatalogT *catalog = FindCatalog(mountpoint);
241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 assert(catalog != NULL);
242
3/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 7 times.
8 if (catalog->mountpoint() == mountpoint) {
243 1 catalog = catalog->parent();
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 assert(catalog != NULL);
245 }
246
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 shash::Any result;
247 uint64_t size;
248
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 catalog->FindNested(mountpoint, &result, &size);
249 16 return result;
250 }
251
252
253 /**
254 * Perform a lookup for a specific DirectoryEntry in the catalogs.
255 * @param path the path to find in the catalogs
256 * @param options whether to perform another lookup to get the parent entry,
257 * too
258 * @param dirent the resulting DirectoryEntry, or special Negative entry
259 * Note: can be set to zero if the result is not important
260 * @return true if lookup succeeded otherwise false
261 */
262 template <class CatalogT>
263 57 bool AbstractCatalogManager<CatalogT>::LookupPath(const PathString &path,
264 const LookupOptions options,
265 DirectoryEntry *dirent)
266 {
267 // initialize as non-negative
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 assert(dirent);
269
2/4
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
57 *dirent = DirectoryEntry();
270
271 // create a dummy negative directory entry
272
1/2
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
57 const DirectoryEntry dirent_negative =
273 DirectoryEntry(catalog::kDirentNegative);
274
275
1/2
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
57 EnforceSqliteMemLimit();
276 57 ReadLock();
277
278
1/2
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
57 CatalogT *best_fit = FindCatalog(path);
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 assert(best_fit != NULL);
280
281 57 perf::Inc(statistics_.n_lookup_path);
282
2/4
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 57 times.
✗ Branch 7 not taken.
57 LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in catalog: '%s'",
283 path.c_str(), best_fit->mountpoint().c_str());
284
1/2
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
57 bool found = best_fit->LookupPath(path, dirent);
285
286 // Possibly in a nested catalog
287
7/8
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 39 times.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 11 times.
✓ Branch 8 taken 46 times.
57 if (!found && MountSubtree(path, best_fit, false /* is_listable */, NULL)) {
288
1/2
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in a nested catalog",
289 path.c_str());
290
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 StageNestedCatalogAndUnlock(path, best_fit, false /* is_listable */);
291 11 WriteLock();
292 // Check again to avoid race
293
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 best_fit = FindCatalog(path);
294
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 assert(best_fit != NULL);
295 11 perf::Inc(statistics_.n_lookup_path);
296
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 found = best_fit->LookupPath(path, dirent);
297
298
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (!found) {
299
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 LogCvmfs(kLogCatalog, kLogDebug,
300 "entry not found, we may have to load nested catalogs");
301
302 CatalogT *nested_catalog;
303 found =
304
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 MountSubtree(path, best_fit, false /* is_listable */, &nested_catalog);
305
306
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (!found) {
307 LogCvmfs(kLogCatalog, kLogDebug,
308 "failed to load nested catalog for '%s'", path.c_str());
309 goto lookup_path_notfound;
310 }
311
312
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (nested_catalog != best_fit) {
313 11 perf::Inc(statistics_.n_lookup_path);
314
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 found = nested_catalog->LookupPath(path, dirent);
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (!found) {
316 LogCvmfs(kLogCatalog, kLogDebug,
317 "nested catalogs loaded but entry '%s' was still not found",
318 path.c_str());
319 if (dirent != NULL) *dirent = dirent_negative;
320 goto lookup_path_notfound;
321 } else {
322 11 best_fit = nested_catalog;
323 }
324 } else {
325 LogCvmfs(kLogCatalog, kLogDebug, "no nested catalog fits");
326 if (dirent != NULL) *dirent = dirent_negative;
327 goto lookup_path_notfound;
328 }
329 }
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 assert(found);
331 }
332 // Not in a nested catalog (because no nested cataog fits), ENOENT
333
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 50 times.
57 if (!found) {
334
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 LogCvmfs(kLogCatalog, kLogDebug, "ENOENT: '%s'", path.c_str());
335
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 if (dirent != NULL) *dirent = dirent_negative;
336 7 goto lookup_path_notfound;
337 }
338
339
2/4
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 50 times.
✗ Branch 7 not taken.
50 LogCvmfs(kLogCatalog, kLogDebug, "found entry '%s' in catalog '%s'",
340 path.c_str(), best_fit->mountpoint().c_str());
341
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if ((options & kLookupRawSymlink) == kLookupRawSymlink) {
343 LinkString raw_symlink;
344 bool retval = best_fit->LookupRawSymlink(path, &raw_symlink);
345 assert(retval); // Must be true, we have just found the entry
346 dirent->set_symlink(raw_symlink);
347 }
348
349 50 Unlock();
350 50 return true;
351
352 7 lookup_path_notfound:
353 7 Unlock();
354 // Includes both: ENOENT and not found due to I/O error
355 7 perf::Inc(statistics_.n_lookup_path_negative);
356 7 return false;
357 57 }
358
359
360 /**
361 * Perform a lookup for Nested Catalog that serves this path.
362 * If the path specified is a catalog mountpoint the catalog at that point is
363 * mounted and returned.
364 * @param path the path to find in the catalogs
365 * @param mountpoint the path to the nested catalog found
366 * @param hash the hash of the nested catalog found
367 * @param size the size of the nested catalog, 0 for root. Root is not a
368 * nested catalog in the database.
369 * @return true if lookup succeeded otherwise false (available catalog failed
370 * to mount)
371 */
372 template <class CatalogT>
373 8 bool AbstractCatalogManager<CatalogT>::LookupNested(
374 const PathString &path,
375 PathString *mountpoint,
376 shash::Any *hash,
377 uint64_t *size
378 ) {
379
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 EnforceSqliteMemLimit();
380 8 bool result = false;
381 8 ReadLock();
382
383 // Look past current path to mount up to intended location
384
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 PathString catalog_path(path);
385
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 catalog_path.Append("/.cvmfscatalog", 14);
386
387 // Find catalog, possibly load nested
388
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 CatalogT *best_fit = FindCatalog(catalog_path);
389 8 CatalogT *catalog = best_fit;
390
3/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 6 times.
8 if (MountSubtree(catalog_path, best_fit, false /* is_listable */, NULL)) {
391
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 StageNestedCatalogAndUnlock(path, best_fit, false);
392 2 WriteLock();
393 // Check again to avoid race
394
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 best_fit = FindCatalog(catalog_path);
395 result =
396
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 MountSubtree(catalog_path, best_fit, false /* is_listable */, &catalog);
397 // Result is false if an available catalog failed to load (error happened)
398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!result) {
399 Unlock();
400 return false;
401 }
402 }
403
404 // If the found catalog is the Root there is no parent to lookup
405
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
8 if (catalog->HasParent()) {
406
2/4
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
7 result = catalog->parent()->FindNested(catalog->root_prefix(), hash, size);
407 }
408
409 // Mountpoint now points to the found catalog
410
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 mountpoint->Assign(catalog->root_prefix());
411
412 // If the result is false, it means that no nested catalog was found for
413 // this path. As the root catalog does not have a Nested Catalog of
414 // itself, we manually set the values and leave the size as 0.
415 // TODO(nhazekam) Allow for Root Catalog to be returned
416
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (!result) {
417 1 *hash = GetRootCatalog()->hash();
418 1 *size = 0;
419 1 result = true;
420 }
421
422 8 Unlock();
423 8 return result;
424 8 }
425
426
427 /**
428 * Create a listing of the parents, catalog, and children of the catalog
429 * that serves the specified path.
430 * If the path specified is a catalog mountpoint the catalog at that point is
431 * mounted and returned.
432 * @param path the path to find in the catalogs
433 * @param result_list the list where the results will be added.
434 * @return true if the list could be created, false if catalog fails to mount.
435 */
436 template <class CatalogT>
437 2 bool AbstractCatalogManager<CatalogT>::ListCatalogSkein(
438 const PathString &path,
439 std::vector<PathString> *result_list
440 ) {
441
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 EnforceSqliteMemLimit();
442 bool result;
443 2 ReadLock();
444
445 // Look past current path to mount up to intended location
446
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 PathString test(path);
447
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 test.Append("/.cvmfscatalog", 14);
448
449 // Find catalog, possibly load nested
450
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 CatalogT *best_fit = FindCatalog(test);
451 2 CatalogT *catalog = best_fit;
452 // True if there is an available nested catalog
453
3/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
2 if (MountSubtree(test, best_fit, false /* is_listable */, NULL)) {
454
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 StageNestedCatalogAndUnlock(path, best_fit, false);
455 1 WriteLock();
456 // Check again to avoid race
457
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 best_fit = FindCatalog(test);
458
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 result = MountSubtree(test, best_fit, false /* is_listable */, &catalog);
459 // result is false if an available catalog failed to load
460
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!result) {
461 Unlock();
462 return false;
463 }
464 }
465
466 // Build listing
467 2 CatalogT *cur_parent = catalog->parent();
468
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (cur_parent) {
469 // Walk up parent tree to find base
470 2 std::vector<catalog::Catalog*> parents;
471
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 while (cur_parent->HasParent()) {
472
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 parents.push_back(cur_parent);
473 1 cur_parent = cur_parent->parent();
474 }
475
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 parents.push_back(cur_parent);
476
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
5 while (!parents.empty()) {
477 // Add to list in order starting at root
478
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 result_list->push_back(parents.back()->root_prefix());
479 3 parents.pop_back();
480 }
481 2 }
482 // Add the current catalog
483
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 result_list->push_back(catalog->root_prefix());
484
485
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 Catalog::NestedCatalogList children = catalog->ListOwnNestedCatalogs();
486
487 // Add all children nested catalogs
488
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 for (unsigned i = 0; i < children.size(); i++) {
489
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 result_list->push_back(children.at(i).mountpoint);
490 }
491
492 2 Unlock();
493 2 return true;
494 2 }
495
496
497 template <class CatalogT>
498 bool AbstractCatalogManager<CatalogT>::LookupXattrs(
499 const PathString &path,
500 XattrList *xattrs)
501 {
502 EnforceSqliteMemLimit();
503 bool result;
504 ReadLock();
505
506 // Find catalog, possibly load nested
507 CatalogT *best_fit = FindCatalog(path);
508 CatalogT *catalog = best_fit;
509 if (MountSubtree(path, best_fit, false /* is_listable */, NULL)) {
510 StageNestedCatalogAndUnlock(path, best_fit, false);
511 WriteLock();
512 // Check again to avoid race
513 best_fit = FindCatalog(path);
514 result = MountSubtree(path, best_fit, false /* is_listable */, &catalog);
515 if (!result) {
516 Unlock();
517 return false;
518 }
519 }
520
521 perf::Inc(statistics_.n_lookup_xattrs);
522 result = catalog->LookupXattrsPath(path, xattrs);
523
524 Unlock();
525 return result;
526 }
527
528
529 /**
530 * Do a listing of the specified directory.
531 * @param path the path of the directory to list
532 * @param listing the resulting DirectoryEntryList
533 * @return true if listing succeeded otherwise false
534 */
535 template <class CatalogT>
536 48 bool AbstractCatalogManager<CatalogT>::Listing(const PathString &path,
537 DirectoryEntryList *listing,
538 const bool expand_symlink)
539 {
540
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 EnforceSqliteMemLimit();
541 bool result;
542 48 ReadLock();
543
544 // Find catalog, possibly load nested
545
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 CatalogT *best_fit = FindCatalog(path);
546 48 CatalogT *catalog = best_fit;
547
3/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 38 times.
48 if (MountSubtree(path, best_fit, true /* is_listable */, NULL)) {
548
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 StageNestedCatalogAndUnlock(path, best_fit, true /* is_listable */);
549 10 WriteLock();
550 // Check again to avoid race
551
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 best_fit = FindCatalog(path);
552
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 result = MountSubtree(path, best_fit, true /* is_listable */, &catalog);
553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!result) {
554 Unlock();
555 return false;
556 }
557 }
558
559 48 perf::Inc(statistics_.n_listing);
560
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 result = catalog->ListingPath(path, listing, expand_symlink);
561
562 48 Unlock();
563 48 return result;
564 }
565
566
567 /**
568 * Do a listing of the specified directory, return only struct stat values.
569 * @param path the path of the directory to list
570 * @param listing the resulting StatEntryList
571 * @return true if listing succeeded otherwise false
572 */
573 template <class CatalogT>
574 2 bool AbstractCatalogManager<CatalogT>::ListingStat(const PathString &path,
575 StatEntryList *listing)
576 {
577
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 EnforceSqliteMemLimit();
578 bool result;
579 2 ReadLock();
580
581 // Find catalog, possibly load nested
582
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 CatalogT *best_fit = FindCatalog(path);
583 2 CatalogT *catalog = best_fit;
584
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (MountSubtree(path, best_fit, true /* is_listable */, NULL)) {
585 StageNestedCatalogAndUnlock(path, best_fit, true /* is_listable */);
586 WriteLock();
587 // Check again to avoid race
588 best_fit = FindCatalog(path);
589 result = MountSubtree(path, best_fit, true /* is_listable */, &catalog);
590 if (!result) {
591 Unlock();
592 return false;
593 }
594 }
595
596 2 perf::Inc(statistics_.n_listing);
597
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 result = catalog->ListingPathStat(path, listing);
598
599 2 Unlock();
600 2 return result;
601 }
602
603
604 /**
605 * Collect file chunks (if exist)
606 * @param path the path of the directory to list
607 * @param interpret_hashes_as hash of the directory entry (by convention the
608 * same than the chunk hashes)
609 * @return true if listing succeeded otherwise false
610 */
611 template <class CatalogT>
612 bool AbstractCatalogManager<CatalogT>::ListFileChunks(
613 const PathString &path,
614 const shash::Algorithms interpret_hashes_as,
615 FileChunkList *chunks)
616 {
617 EnforceSqliteMemLimit();
618 bool result;
619 ReadLock();
620
621 // Find catalog, possibly load nested
622 CatalogT *best_fit = FindCatalog(path);
623 CatalogT *catalog = best_fit;
624 if (MountSubtree(path, best_fit, false /* is_listable */, NULL)) {
625 StageNestedCatalogAndUnlock(path, best_fit, false);
626 WriteLock();
627 // Check again to avoid race
628 best_fit = FindCatalog(path);
629 result = MountSubtree(path, best_fit, false /* is_listable */, &catalog);
630 if (!result) {
631 Unlock();
632 return false;
633 }
634 }
635
636 result = catalog->ListPathChunks(path, interpret_hashes_as, chunks);
637
638 Unlock();
639 return result;
640 }
641
642 template <class CatalogT>
643 3 catalog::Counters AbstractCatalogManager<CatalogT>::LookupCounters(
644 const PathString &path,
645 std::string *subcatalog_path,
646 shash::Any *hash
647 )
648 {
649
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 EnforceSqliteMemLimit();
650 bool result;
651 3 ReadLock();
652
653 // Look past current path to mount up to intended location
654
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 PathString catalog_path(path);
655
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 catalog_path.Append("/.cvmfscatalog", 14);
656
657 // Find catalog, possibly load nested
658
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 CatalogT *best_fit = FindCatalog(catalog_path);
659 3 CatalogT *catalog = best_fit;
660
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (MountSubtree(catalog_path, best_fit, false /* is_listable */, NULL)) {
661 StageNestedCatalogAndUnlock(path, best_fit, false /* is_listable */);
662 WriteLock();
663 // Check again to avoid race
664 best_fit = FindCatalog(catalog_path);
665 result =
666 MountSubtree(catalog_path, best_fit, false /* is_listable */, &catalog);
667 // Result is false if an available catalog failed to load (error happened)
668 if (!result) {
669 Unlock();
670 *subcatalog_path = "error: failed to load catalog!";
671 *hash = shash::Any();
672 return catalog::Counters();
673 }
674 }
675
676 3 *hash = catalog->hash();
677
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 *subcatalog_path = catalog->mountpoint().ToString();
678 3 catalog::Counters counters = catalog->GetCounters();
679 3 Unlock();
680 3 return counters;
681 3 }
682
683
684 template <class CatalogT>
685 33 uint64_t AbstractCatalogManager<CatalogT>::GetRevision() const {
686 33 ReadLock();
687 33 const uint64_t revision = GetRevisionNoLock();
688 33 Unlock();
689
690 33 return revision;
691 }
692
693 /**
694 * Like GetRevision() only without any locking mechanism.
695 * As such should only be used in conditions where a lock was already taken
696 * and calling GetRevision() would otherwise result in a deadlock.
697 */
698 template <class CatalogT>
699 59 uint64_t AbstractCatalogManager<CatalogT>::GetRevisionNoLock() const {
700 59 return revision_cache_;
701 }
702
703 template <class CatalogT>
704 uint64_t AbstractCatalogManager<CatalogT>::GetTimestamp() const {
705 ReadLock();
706 const uint64_t timestamp = GetTimestampNoLock();
707 Unlock();
708
709 return timestamp;
710 }
711
712 /**
713 * Like GetTimestamp() only without any locking mechanism.
714 * As such should only be used in conditions where a lock was already taken
715 * and calling GetTimestamp() would otherwise result in a deadlock.
716 */
717 template <class CatalogT>
718 6 uint64_t AbstractCatalogManager<CatalogT>::GetTimestampNoLock() const {
719 6 return timestamp_cache_;
720 }
721
722 template <class CatalogT>
723 25 bool AbstractCatalogManager<CatalogT>::GetVOMSAuthz(std::string *authz) const {
724 25 ReadLock();
725 25 const bool has_authz = has_authz_cache_;
726
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
25 if (has_authz && authz)
727 *authz = authz_cache_;
728 25 Unlock();
729 25 return has_authz;
730 }
731
732
733 template <class CatalogT>
734 bool AbstractCatalogManager<CatalogT>::HasExplicitTTL() const {
735 ReadLock();
736 const bool result = GetRootCatalog()->HasExplicitTTL();
737 Unlock();
738 return result;
739 }
740
741
742 template <class CatalogT>
743 1 uint64_t AbstractCatalogManager<CatalogT>::GetTTL() const {
744 1 ReadLock();
745 1 const uint64_t ttl = GetRootCatalog()->GetTTL();
746 1 Unlock();
747 1 return ttl;
748 }
749
750
751 template <class CatalogT>
752 33 int AbstractCatalogManager<CatalogT>::GetNumCatalogs() const {
753 33 ReadLock();
754 33 int result = catalogs_.size();
755 33 Unlock();
756 33 return result;
757 }
758
759
760 /**
761 * Gets a formatted tree of the currently attached catalogs
762 */
763 template <class CatalogT>
764 string AbstractCatalogManager<CatalogT>::PrintHierarchy() const {
765 ReadLock();
766 string output = PrintHierarchyRecursively(GetRootCatalog(), 0);
767 Unlock();
768 return output;
769 }
770
771
772 /**
773 * Assigns the next free numbers in the 64 bit space
774 */
775 template <class CatalogT>
776 130 InodeRange AbstractCatalogManager<CatalogT>::AcquireInodes(uint64_t size) {
777 130 InodeRange result;
778 130 result.offset = inode_gauge_;
779 130 result.size = size;
780
781 130 inode_gauge_ += size;
782 130 LogCvmfs(kLogCatalog, kLogDebug, "allocating inodes from %lu to %lu.",
783
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 result.offset + 1, inode_gauge_);
784
785 130 return result;
786 }
787
788
789 /**
790 * Called if a catalog is detached which renders the associated InodeChunk
791 * invalid.
792 * @param chunk the InodeChunk to be freed
793 */
794 template <class CatalogT>
795 130 void AbstractCatalogManager<CatalogT>::ReleaseInodes(const InodeRange chunk) {
796 // TODO(jblomer) currently inodes are only released on remount
797 130 }
798
799
800 /**
801 * Find the catalog leaf in the tree that fits the path.
802 * The path might be served by a not yet loaded nested catalog.
803 * @param path the path a catalog is searched for
804 * @return the catalog which is best fitting at the given path
805 */
806 template <class CatalogT>
807 487 CatalogT* AbstractCatalogManager<CatalogT>::FindCatalog(
808 const PathString &path) const {
809
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 487 times.
487 assert(catalogs_.size() > 0);
810
811 // Start at the root catalog and successively go down the catalog tree
812 487 CatalogT *best_fit = GetRootCatalog();
813 487 CatalogT *next_fit = NULL;
814
5/5
✓ Branch 2 taken 71 times.
✓ Branch 3 taken 436 times.
✓ Branch 4 taken 64 times.
✓ Branch 5 taken 63 times.
✓ Branch 6 taken 8 times.
571 while (best_fit->mountpoint() != path) {
815 499 next_fit = best_fit->FindSubtree(path);
816
2/2
✓ Branch 0 taken 415 times.
✓ Branch 1 taken 84 times.
499 if (next_fit == NULL)
817 415 break;
818 84 best_fit = next_fit;
819 }
820
821 487 return best_fit;
822 }
823
824
825 /**
826 * Checks if a searched catalog is already mounted to this CatalogManager
827 * @param root_path the root path of the searched catalog
828 * @param attached_catalog is set to the searched catalog, if not NULL
829 * @return true if catalog is already present, false otherwise
830 */
831 template <class CatalogT>
832 108 bool AbstractCatalogManager<CatalogT>::IsAttached(const PathString &root_path,
833 CatalogT **attached_catalog) const
834 {
835
2/2
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 36 times.
108 if (catalogs_.size() == 0)
836 72 return false;
837
838 36 CatalogT *best_fit = FindCatalog(root_path);
839
4/5
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 17 times.
✓ Branch 6 taken 4 times.
36 if (best_fit->mountpoint() != root_path)
840 32 return false;
841
842
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (attached_catalog != NULL) *attached_catalog = best_fit;
843 4 return true;
844 }
845
846
847 template <class CatalogT>
848 24 void AbstractCatalogManager<CatalogT>::StageNestedCatalogAndUnlock(
849 const PathString &path,
850 const CatalogT *parent,
851 bool is_listable)
852 {
853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 assert(parent);
854 24 const unsigned path_len = path.GetLength();
855
856 24 perf::Inc(statistics_.n_nested_listing);
857 typedef typename CatalogT::NestedCatalogList NestedCatalogList;
858 24 const NestedCatalogList& nested_catalogs = parent->ListNestedCatalogs();
859
860 48 for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(),
861
1/2
✓ Branch 3 taken 29 times.
✗ Branch 4 not taken.
53 iEnd = nested_catalogs.end(); i != iEnd; ++i)
862 {
863
2/2
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 24 times.
29 if (!path.StartsWith(i->mountpoint))
864 5 continue;
865
866 // in this case the path doesn't start with
867 // the mountpoint in a file path sense
868 // (e.g. path is /a/bc and mountpoint is /a/b), and will be ignored
869 24 const unsigned mountpoint_len = i->mountpoint.GetLength();
870
4/6
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
24 if (path_len > mountpoint_len && path.GetChars()[mountpoint_len] != '/')
871 continue;
872
873 // Found a nested catalog transition point
874
4/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
24 if (!is_listable && (path_len == mountpoint_len))
875 2 break;
876
877 22 Unlock();
878
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 22 times.
✗ Branch 7 not taken.
44 LogCvmfs(kLogCatalog, kLogDebug, "staging nested catalog at %s (%s)",
879 44 i->mountpoint.c_str(), i->hash.ToString().c_str());
880
1/2
✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
22 StageNestedCatalogByHash(i->hash, i->mountpoint);
881 22 return;
882 }
883 2 Unlock();
884 }
885
886 /**
887 * Recursively mounts all nested catalogs required to serve a path.
888 * If leaf_catalog is NULL, just indicate if it is necessary to load a
889 * nested catalog for the given path.
890 * The final leaf nested catalog is returned.
891 * The is_listable parameter is relevant if path is a nested catalog. Only
892 * if is_listable is true, the nested catalog will be used; otherwise the parent
893 * with the transaction point is sufficient.
894 */
895 template <class CatalogT>
896 440 bool AbstractCatalogManager<CatalogT>::MountSubtree(
897 const PathString &path,
898 const CatalogT *entry_point,
899 bool is_listable,
900 CatalogT **leaf_catalog)
901 {
902 440 bool result = true;
903
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 440 times.
440 CatalogT *parent = (entry_point == NULL) ?
904 GetRootCatalog() : const_cast<CatalogT *>(entry_point);
905
2/5
✓ Branch 1 taken 440 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 440 times.
440 assert(path.StartsWith(parent->mountpoint()));
906
907 440 unsigned path_len = path.GetLength();
908
909 // Try to find path as a super string of nested catalog mount points
910 440 perf::Inc(statistics_.n_nested_listing);
911 typedef typename CatalogT::NestedCatalogList NestedCatalogList;
912 const NestedCatalogList& nested_catalogs =
913
1/2
✓ Branch 1 taken 384 times.
✗ Branch 2 not taken.
440 parent->ListNestedCatalogs();
914 880 for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(),
915
2/2
✓ Branch 3 taken 155 times.
✓ Branch 4 taken 378 times.
973 iEnd = nested_catalogs.end(); i != iEnd; ++i)
916 {
917 // Next nesting level
918
2/2
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 90 times.
155 if (path.StartsWith(i->mountpoint)) {
919 // in this case the path doesn't start with
920 // the mountpoint in a file path sense
921 // (e.g. path is /a/bc and mountpoint is /a/b), and will be ignored
922 65 unsigned mountpoint_len = i->mountpoint.GetLength();
923
6/6
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 19 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 43 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 62 times.
65 if (path_len > mountpoint_len && path.GetChars()[mountpoint_len] != '/')
924 3 continue;
925
926 // Found a nested catalog transition point
927
4/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 32 times.
62 if (!is_listable && (path_len == mountpoint_len))
928 2 break;
929
930
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 36 times.
60 if (leaf_catalog == NULL)
931 24 return true;
932 CatalogT *new_nested;
933
1/2
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
36 LogCvmfs(kLogCatalog, kLogDebug, "load nested catalog at %s",
934 36 i->mountpoint.c_str());
935 // prevent endless recursion with corrupted catalogs
936 // (due to reloading root)
937
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
36 if (i->hash.IsNull())
938 return false;
939
1/2
✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
36 new_nested = MountCatalog(i->mountpoint, i->hash, parent);
940
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (!new_nested)
941 return false;
942
943
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 result = MountSubtree(path, new_nested, is_listable, &parent);
944 36 break;
945 }
946 }
947
948
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 359 times.
416 if (leaf_catalog == NULL)
949 57 return false;
950 359 *leaf_catalog = parent;
951 359 return result;
952 }
953
954
955 /**
956 * Load a catalog file and attach it to the tree of Catalog objects.
957 * Loading of catalogs is implemented by derived classes.
958 */
959 template <class CatalogT>
960 108 CatalogT *AbstractCatalogManager<CatalogT>::MountCatalog(
961 const PathString &mountpoint,
962 const shash::Any &hash,
963 CatalogT *parent_catalog)
964 {
965 108 CatalogT *attached_catalog = NULL;
966
3/4
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 104 times.
108 if (IsAttached(mountpoint, &attached_catalog)) {
967 4 return attached_catalog;
968 }
969
970
1/2
✓ Branch 1 taken 104 times.
✗ Branch 2 not taken.
104 CatalogContext ctlg_context(hash, mountpoint, kCtlgLocationMounted);
971
972
7/9
✓ Branch 1 taken 104 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 57 times.
✓ Branch 7 taken 15 times.
✓ Branch 8 taken 57 times.
✓ Branch 9 taken 47 times.
104 if (ctlg_context.IsRootCatalog() && hash.IsNull()) {
973
3/4
✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 52 times.
57 if (GetNewRootCatalogContext(&ctlg_context) == kLoadFail) {
974
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 LogCvmfs(kLogCatalog, kLogDebug,
975 "failed to retrieve valid root catalog '%s'",
976 mountpoint.c_str());
977 5 return NULL;
978 }
979 }
980
981
1/2
✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
99 const LoadReturn retval = LoadCatalogByHash(&ctlg_context);
982
2/4
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 99 times.
99 if ((retval == kLoadFail) || (retval == kLoadNoSpace)) {
983 LogCvmfs(kLogCatalog, kLogDebug, "failed to load catalog '%s' (%d - %s)",
984 mountpoint.c_str(), retval, Code2Ascii(retval));
985 return NULL;
986 }
987
988
2/4
✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
99 attached_catalog = CreateCatalog(ctlg_context.mountpoint(),
989 99 ctlg_context.hash(),
990 parent_catalog);
991
992 // Attach loaded catalog
993
3/7
✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 99 times.
99 if (!AttachCatalog(ctlg_context.sqlite_path(), attached_catalog)) {
994 LogCvmfs(kLogCatalog, kLogDebug, "failed to attach catalog '%s'",
995 mountpoint.c_str());
996 UnloadCatalog(attached_catalog);
997 return NULL;
998 }
999
1000
6/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 92 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 96 times.
99 if ((catalog_watermark_ > 0) && (catalogs_.size() >= catalog_watermark_)) {
1001
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 DetachSiblings(mountpoint);
1002 }
1003
1004 99 return attached_catalog;
1005 104 }
1006
1007
1008 /**
1009 * Load a catalog file as a freestanding Catalog object.
1010 * Loading of catalogs is implemented by derived classes.
1011 */
1012 template <class CatalogT>
1013 8 CatalogT *AbstractCatalogManager<CatalogT>::LoadFreeCatalog(
1014 const PathString &mountpoint,
1015 const shash::Any &hash)
1016 {
1017
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 assert(!hash.IsNull());
1018
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 CatalogContext ctlg_context(hash, mountpoint, kCtlgNoLocationNeeded);
1019
1020
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
8 const LoadReturn load_ret = LoadCatalogByHash(&ctlg_context);
1021
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (load_ret != kLoadNew) {
1023 return NULL;
1024 }
1025
1026
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
7 CatalogT *catalog = CatalogT::AttachFreely(mountpoint.ToString(),
1027 ctlg_context.sqlite_path(),
1028 7 ctlg_context.hash());
1029
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 catalog->TakeDatabaseFileOwnership();
1030 7 return catalog;
1031 8 }
1032
1033
1034 /**
1035 * Attaches a newly created catalog.
1036 * @param db_path the file on a local file system containing the database
1037 * @param new_catalog the catalog to attach to this CatalogManager
1038 * @return true on success, false otherwise
1039 */
1040 template <class CatalogT>
1041 130 bool AbstractCatalogManager<CatalogT>::AttachCatalog(const string &db_path,
1042 CatalogT *new_catalog)
1043 {
1044
1/2
✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
130 LogCvmfs(kLogCatalog, kLogDebug, "attaching catalog file %s",
1045 db_path.c_str());
1046
1047 // Initialize the new catalog
1048
2/4
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 130 times.
130 if (!new_catalog->OpenDatabase(db_path)) {
1049 LogCvmfs(kLogCatalog, kLogDebug, "initialization of catalog %s failed",
1050 db_path.c_str());
1051 return false;
1052 }
1053
1054 // Determine the inode offset of this catalog
1055 130 uint64_t inode_chunk_size = new_catalog->max_row_id();
1056
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 InodeRange range = AcquireInodes(inode_chunk_size);
1057 130 new_catalog->set_inode_range(range);
1058
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
130 new_catalog->SetInodeAnnotation(inode_annotation_);
1059
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
130 new_catalog->SetOwnerMaps(&uid_map_, &gid_map_);
1060
1061 // Add catalog to the manager
1062
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 130 times.
130 if (!new_catalog->IsInitialized()) {
1063 LogCvmfs(kLogCatalog, kLogDebug,
1064 "catalog initialization failed (obscure data)");
1065 inode_gauge_ -= inode_chunk_size;
1066 return false;
1067 }
1068
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 CheckInodeWatermark();
1069
1070 // The revision of the catalog tree is given by the root catalog revision
1071
2/2
✓ Branch 1 taken 69 times.
✓ Branch 2 taken 61 times.
130 if (catalogs_.empty()) {
1072
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
69 revision_cache_ = new_catalog->GetRevision();
1073
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
69 timestamp_cache_ = new_catalog->GetLastModified();
1074 69 statistics_.catalog_revision->Set(revision_cache_);
1075
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
69 has_authz_cache_ = new_catalog->GetVOMSAuthz(&authz_cache_);
1076 69 volatile_flag_ = new_catalog->volatile_flag();
1077 }
1078
1079
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 catalogs_.push_back(new_catalog);
1080
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 ActivateCatalog(new_catalog);
1081 130 return true;
1082 }
1083
1084
1085 /**
1086 * Removes a catalog from this CatalogManager, the catalog pointer is
1087 * freed if the call succeeds.
1088 * This method can create dangling children if a catalog in the middle of
1089 * a tree is removed.
1090 * @param catalog the catalog to detach
1091 * @return true on success, false otherwise
1092 */
1093 template <class CatalogT>
1094 130 void AbstractCatalogManager<CatalogT>::DetachCatalog(CatalogT *catalog) {
1095
2/2
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 70 times.
130 if (catalog->HasParent())
1096
1/2
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
60 catalog->parent()->RemoveChild(catalog);
1097
1098 130 ReleaseInodes(catalog->inode_range());
1099
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 UnloadCatalog(catalog);
1100
1101 // Delete catalog from internal lists
1102 130 typename CatalogList::iterator i;
1103 130 typename CatalogList::const_iterator iend;
1104
1/2
✓ Branch 5 taken 222 times.
✗ Branch 6 not taken.
222 for (i = catalogs_.begin(), iend = catalogs_.end(); i != iend; ++i) {
1105
2/2
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 92 times.
222 if (*i == catalog) {
1106
1/2
✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
130 catalogs_.erase(i);
1107
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 delete catalog;
1108 130 return;
1109 }
1110 }
1111
1112 assert(false);
1113 }
1114
1115
1116 /**
1117 * Removes a catalog (and all of it's children) from this CatalogManager.
1118 * The given catalog and all children are freed, if this call succeeds.
1119 * @param catalog the catalog to detach
1120 * @return true on success, false otherwise
1121 */
1122 template <class CatalogT>
1123 129 void AbstractCatalogManager<CatalogT>::DetachSubtree(CatalogT *catalog) {
1124 // Detach all child catalogs recursively
1125 129 typename CatalogList::const_iterator i;
1126 129 typename CatalogList::const_iterator iend;
1127
1/2
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
129 CatalogList catalogs_to_detach = catalog->GetChildren();
1128 129 for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end();
1129
2/2
✓ Branch 2 taken 55 times.
✓ Branch 3 taken 129 times.
184 i != iend; ++i)
1130 {
1131
1/2
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
55 DetachSubtree(*i);
1132 }
1133
1134
1/2
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
129 DetachCatalog(catalog);
1135 129 }
1136
1137
1138 /**
1139 * Detaches all nested catalogs that are not on a prefix of the given tree.
1140 * Used when the catalog_watermark_ is surpassed.
1141 */
1142 template <class CatalogT>
1143 4 void AbstractCatalogManager<CatalogT>::DetachSiblings(
1144 const PathString &current_tree)
1145 {
1146 bool again;
1147
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 do {
1148 4 again = false;
1149 4 unsigned N = catalogs_.size();
1150
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 for (unsigned i = 0; i < N; ++i) {
1151
5/9
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 9 times.
✗ Branch 9 not taken.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 8 times.
9 if (!HasPrefix(current_tree.ToString(),
1152 9 catalogs_[i]->mountpoint().ToString(),
1153 false /* ignore_case */))
1154 {
1155 1 DetachSubtree(catalogs_[i]);
1156 1 again = true;
1157 1 break;
1158 }
1159 }
1160 } while (again);
1161 3 perf::Inc(statistics_.n_detach_siblings);
1162 3 }
1163
1164
1165 /**
1166 * Formats the catalog hierarchy
1167 */
1168 template <class CatalogT>
1169 string AbstractCatalogManager<CatalogT>::PrintHierarchyRecursively(
1170 const CatalogT *catalog,
1171 const int level) const
1172 {
1173 string output;
1174
1175 // Indent according to level
1176 for (int i = 0; i < level; ++i)
1177 output += " ";
1178
1179 output += "-> " + string(catalog->mountpoint().GetChars(),
1180 catalog->mountpoint().GetLength())
1181 + "\n";
1182
1183 CatalogList children = catalog->GetChildren();
1184 typename CatalogList::const_iterator i = children.begin();
1185 typename CatalogList::const_iterator iend = children.end();
1186 for (; i != iend; ++i) {
1187 output += PrintHierarchyRecursively(*i, level + 1);
1188 }
1189
1190 return output;
1191 }
1192
1193
1194 template <class CatalogT>
1195 std::string AbstractCatalogManager<CatalogT>::PrintMemStatsRecursively(
1196 const CatalogT *catalog) const
1197 {
1198 string result = catalog->PrintMemStatistics() + "\n";
1199
1200 CatalogList children = catalog->GetChildren();
1201 typename CatalogList::const_iterator i = children.begin();
1202 typename CatalogList::const_iterator iend = children.end();
1203 for (; i != iend; ++i) {
1204 result += PrintMemStatsRecursively(*i);
1205 }
1206 return result;
1207 }
1208
1209
1210 /**
1211 * Statistics from all catalogs
1212 */
1213 template <class CatalogT>
1214 std::string AbstractCatalogManager<CatalogT>::PrintAllMemStatistics() const {
1215 string result;
1216 ReadLock();
1217 result = PrintMemStatsRecursively(GetRootCatalog());
1218 Unlock();
1219 return result;
1220 }
1221
1222
1223 template <class CatalogT>
1224 70 void AbstractCatalogManager<CatalogT>::EnforceSqliteMemLimit() {
1225 char *mem_enforced =
1226 70 static_cast<char *>(pthread_getspecific(pkey_sqlitemem_));
1227
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 53 times.
70 if (mem_enforced == NULL) {
1228 17 sqlite3_soft_heap_limit(kSqliteMemPerThread);
1229 17 pthread_setspecific(pkey_sqlitemem_, this);
1230 }
1231 70 }
1232
1233 } // namespace catalog
1234
1235
1236
1237
1238 #endif // CVMFS_CATALOG_MGR_IMPL_H_
1239 //NOLINTEND
1240