GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_mgr_impl.h
Date: 2026-06-28 02:36:10
Exec Total Coverage
Lines: 426 580 73.4%
Branches: 295 654 45.1%

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