GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_mgr_impl.h
Date: 2026-07-19 02:35:15
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 1755 AbstractCatalogManager<CatalogT>::AbstractCatalogManager(
31 perf::Statistics *statistics)
32
1/2
✓ Branch 4 taken 1755 times.
✗ Branch 5 not taken.
1755 : statistics_(statistics) {
33 1755 inode_watermark_status_ = 0;
34 1755 inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset;
35 1755 revision_cache_ = 0;
36 1755 timestamp_cache_ = 0;
37 1755 catalog_watermark_ = 0;
38 1755 volatile_flag_ = false;
39 1755 has_authz_cache_ = false;
40 1755 inode_annotation_ = NULL;
41 1755 incarnation_ = 0;
42 1755 rwlock_ = reinterpret_cast<pthread_rwlock_t *>(
43 1755 smalloc(sizeof(pthread_rwlock_t)));
44 1755 int retval = pthread_rwlock_init(rwlock_, NULL);
45
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1755 times.
1755 assert(retval == 0);
46 1755 retval = pthread_key_create(&pkey_sqlitemem_, NULL);
47
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1755 times.
1755 assert(retval == 0);
48 1755 }
49
50 template<class CatalogT>
51 3506 AbstractCatalogManager<CatalogT>::~AbstractCatalogManager() {
52 3506 DetachAll();
53 3506 pthread_key_delete(pkey_sqlitemem_);
54 3506 pthread_rwlock_destroy(rwlock_);
55 3506 free(rwlock_);
56 }
57
58 template<class CatalogT>
59 252 void AbstractCatalogManager<CatalogT>::SetInodeAnnotation(
60 InodeAnnotation *new_annotation) {
61
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 252 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
252 assert(catalogs_.empty() || (new_annotation == inode_annotation_));
62 252 inode_annotation_ = new_annotation;
63 252 }
64
65 template<class CatalogT>
66 631 void AbstractCatalogManager<CatalogT>::SetOwnerMaps(const OwnerMap &uid_map,
67 const OwnerMap &gid_map) {
68 631 uid_map_ = uid_map;
69 631 gid_map_ = gid_map;
70 631 }
71
72 template<class CatalogT>
73 510 void AbstractCatalogManager<CatalogT>::SetCatalogWatermark(unsigned limit) {
74 510 catalog_watermark_ = limit;
75 510 }
76
77 template<class CatalogT>
78 3257 void AbstractCatalogManager<CatalogT>::CheckInodeWatermark() {
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3257 times.
3257 if (inode_watermark_status_ > 0)
80 return;
81
82 3257 uint64_t highest_inode = inode_gauge_;
83
2/2
✓ Branch 0 taken 222 times.
✓ Branch 1 taken 3035 times.
3257 if (inode_annotation_)
84 222 highest_inode += inode_annotation_->GetGeneration();
85 3257 uint64_t uint32_border = 1;
86 3257 uint32_border = uint32_border << 32;
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3257 times.
3257 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 1257 bool AbstractCatalogManager<CatalogT>::Init() {
100 1257 LogCvmfs(kLogCatalog, kLogDebug, "Initialize catalog");
101 1257 WriteLock();
102
2/4
✓ Branch 2 taken 1257 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1257 times.
✗ Branch 6 not taken.
1257 bool const attached = MountCatalog(PathString("", 0), shash::Any(), NULL);
103 1257 Unlock();
104
105
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 1145 times.
1257 if (!attached) {
106 112 LogCvmfs(kLogCatalog, kLogDebug, "failed to initialize root catalog");
107 }
108
109 1257 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 8 LoadReturn AbstractCatalogManager<CatalogT>::RemountDryrun() {
120
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogCatalog, kLogDebug, "dryrun remounting repositories");
121
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 CatalogContext ctlg_context;
122
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 return GetNewRootCatalogContext(&ctlg_context);
123 8 }
124
125 template<class CatalogT>
126 8 LoadReturn AbstractCatalogManager<CatalogT>::Remount() {
127
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogCatalog, kLogDebug, "remounting repositories");
128
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 CatalogContext ctlg_context;
129
130
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (GetNewRootCatalogContext(&ctlg_context) != kLoadNew
131
5/8
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
8 && 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 8 WriteLock();
139
140
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const LoadReturn load_error = LoadCatalogByHash(&ctlg_context);
141
142
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 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 8 times.
✗ Branch 2 not taken.
8 CheckInodeWatermark();
158 8 Unlock();
159
160 8 return load_error;
161 8 }
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 132 void AbstractCatalogManager<CatalogT>::DetachNested() {
209 132 WriteLock();
210
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 132 times.
132 if (catalogs_.empty()) {
211 Unlock();
212 return;
213 }
214
215 132 typename CatalogList::const_iterator i;
216 132 typename CatalogList::const_iterator iend;
217
1/2
✓ Branch 2 taken 132 times.
✗ Branch 3 not taken.
132 CatalogList catalogs_to_detach = GetRootCatalog()->GetChildren();
218 132 for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end();
219
2/2
✓ Branch 2 taken 106 times.
✓ Branch 3 taken 132 times.
238 i != iend; ++i) {
220
1/2
✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
106 DetachSubtree(*i);
221 }
222
223 132 Unlock();
224 132 }
225
226
227 /**
228 * Returns the NULL hash if the nested catalog is not found.
229 */
230 template<class CatalogT>
231 261 shash::Any AbstractCatalogManager<CatalogT>::GetNestedCatalogHash(
232 const PathString &mountpoint) {
233
2/4
✓ Branch 1 taken 261 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 261 times.
261 assert(!mountpoint.IsEmpty());
234
1/2
✓ Branch 1 taken 261 times.
✗ Branch 2 not taken.
261 CatalogT *catalog = FindCatalog(mountpoint);
235
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
261 assert(catalog != NULL);
236
3/4
✓ Branch 1 taken 261 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 255 times.
261 if (catalog->mountpoint() == mountpoint) {
237 6 catalog = catalog->parent();
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 assert(catalog != NULL);
239 }
240
1/2
✓ Branch 1 taken 261 times.
✗ Branch 2 not taken.
261 shash::Any result;
241 uint64_t size;
242
1/2
✓ Branch 1 taken 261 times.
✗ Branch 2 not taken.
261 catalog->FindNested(mountpoint, &result, &size);
243 522 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 1757 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 1757 times.
1757 assert(dirent);
262
2/4
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1757 times.
✗ Branch 5 not taken.
1757 *dirent = DirectoryEntry();
263
264 // create a dummy negative directory entry
265
1/2
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
1757 const DirectoryEntry dirent_negative = DirectoryEntry(
266 catalog::kDirentNegative);
267
268
1/2
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
1757 EnforceSqliteMemLimit();
269 1757 ReadLock();
270
271
1/2
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
1757 CatalogT *best_fit = FindCatalog(path);
272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1757 times.
1757 assert(best_fit != NULL);
273
274 1757 perf::Inc(statistics_.n_lookup_path);
275
2/4
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 1757 times.
✗ Branch 7 not taken.
1757 LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in catalog: '%s'",
276 path.c_str(), best_fit->mountpoint().c_str());
277
1/2
✓ Branch 1 taken 1757 times.
✗ Branch 2 not taken.
1757 bool found = best_fit->LookupPath(path, dirent);
278
279 // Possibly in a nested catalog
280
7/8
✓ Branch 0 taken 458 times.
✓ Branch 1 taken 1299 times.
✓ Branch 3 taken 458 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 252 times.
✓ Branch 6 taken 206 times.
✓ Branch 7 taken 252 times.
✓ Branch 8 taken 1505 times.
1757 if (!found && MountSubtree(path, best_fit, false /* is_listable */, NULL)) {
281
1/2
✓ Branch 2 taken 252 times.
✗ Branch 3 not taken.
252 LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in a nested catalog",
282 path.c_str());
283
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 StageNestedCatalogAndUnlock(path, best_fit, false /* is_listable */);
284 252 WriteLock();
285 // Check again to avoid race
286
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 best_fit = FindCatalog(path);
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252 assert(best_fit != NULL);
288 252 perf::Inc(statistics_.n_lookup_path);
289
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 found = best_fit->LookupPath(path, dirent);
290
291
1/2
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
252 if (!found) {
292
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 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 252 times.
✗ Branch 2 not taken.
252 found = MountSubtree(path, best_fit, false /* is_listable */,
297 &nested_catalog);
298
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252 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 252 times.
✗ Branch 1 not taken.
252 if (nested_catalog != best_fit) {
306 252 perf::Inc(statistics_.n_lookup_path);
307
1/2
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
252 found = nested_catalog->LookupPath(path, dirent);
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252 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 252 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 252 times.
252 assert(found);
326 }
327 // Not in a nested catalog (because no nested cataog fits), ENOENT
328
2/2
✓ Branch 0 taken 206 times.
✓ Branch 1 taken 1551 times.
1757 if (!found) {
329
1/2
✓ Branch 2 taken 206 times.
✗ Branch 3 not taken.
206 LogCvmfs(kLogCatalog, kLogDebug, "ENOENT: '%s'", path.c_str());
330
1/2
✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
206 if (dirent != NULL)
331
1/2
✓ Branch 1 taken 206 times.
✗ Branch 2 not taken.
206 *dirent = dirent_negative;
332 206 goto lookup_path_notfound;
333 }
334
335
2/4
✓ Branch 1 taken 1551 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 1551 times.
✗ Branch 7 not taken.
1551 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 1551 times.
1551 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 1551 Unlock();
346 1551 return true;
347
348 206 lookup_path_notfound:
349 206 Unlock();
350 // Includes both: ENOENT and not found due to I/O error
351 206 perf::Inc(statistics_.n_lookup_path_negative);
352 206 return false;
353 1757 }
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 382 bool AbstractCatalogManager<CatalogT>::LookupNested(const PathString &path,
370 PathString *mountpoint,
371 shash::Any *hash,
372 uint64_t *size) {
373
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
382 EnforceSqliteMemLimit();
374 382 bool result = false;
375 382 ReadLock();
376
377 // Look past current path to mount up to intended location
378
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
382 PathString catalog_path(path);
379
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
382 catalog_path.Append("/.cvmfscatalog", 14);
380
381 // Find catalog, possibly load nested
382
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
382 CatalogT *best_fit = FindCatalog(catalog_path);
383 382 CatalogT *catalog = best_fit;
384
3/4
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 100 times.
✓ Branch 4 taken 282 times.
382 if (MountSubtree(catalog_path, best_fit, false /* is_listable */, NULL)) {
385
1/2
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
100 StageNestedCatalogAndUnlock(path, best_fit, false);
386 100 WriteLock();
387 // Check again to avoid race
388
1/2
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
100 best_fit = FindCatalog(catalog_path);
389
1/2
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
100 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 100 times.
100 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 334 times.
✓ Branch 2 taken 48 times.
382 if (catalog->HasParent()) {
400
2/4
✓ Branch 2 taken 334 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 334 times.
✗ Branch 6 not taken.
334 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 382 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 382 times.
✗ Branch 5 not taken.
382 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 48 times.
✓ Branch 1 taken 334 times.
382 if (!result) {
411 48 *hash = GetRootCatalog()->hash();
412 48 *size = 0;
413 48 result = true;
414 }
415
416 382 Unlock();
417 382 return result;
418 382 }
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 98 bool AbstractCatalogManager<CatalogT>::ListCatalogSkein(
432 const PathString &path, std::vector<PathString> *result_list) {
433
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 EnforceSqliteMemLimit();
434 bool result;
435 98 ReadLock();
436
437 // Look past current path to mount up to intended location
438
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 PathString test(path);
439
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 test.Append("/.cvmfscatalog", 14);
440
441 // Find catalog, possibly load nested
442
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 CatalogT *best_fit = FindCatalog(test);
443 98 CatalogT *catalog = best_fit;
444 // True if there is an available nested catalog
445
3/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 49 times.
98 if (MountSubtree(test, best_fit, false /* is_listable */, NULL)) {
446
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 StageNestedCatalogAndUnlock(path, best_fit, false);
447 49 WriteLock();
448 // Check again to avoid race
449
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 best_fit = FindCatalog(test);
450
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 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 49 times.
49 if (!result) {
453 Unlock();
454 return false;
455 }
456 }
457
458 // Build listing
459 98 CatalogT *cur_parent = catalog->parent();
460
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 if (cur_parent) {
461 // Walk up parent tree to find base
462 98 std::vector<catalog::Catalog *> parents;
463
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 98 times.
147 while (cur_parent->HasParent()) {
464
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 parents.push_back(cur_parent);
465 49 cur_parent = cur_parent->parent();
466 }
467
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 parents.push_back(cur_parent);
468
2/2
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 98 times.
245 while (!parents.empty()) {
469 // Add to list in order starting at root
470
2/4
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 147 times.
✗ Branch 6 not taken.
147 result_list->push_back(parents.back()->root_prefix());
471 147 parents.pop_back();
472 }
473 98 }
474 // Add the current catalog
475
2/4
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
98 result_list->push_back(catalog->root_prefix());
476
477
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 Catalog::NestedCatalogList children = catalog->ListOwnNestedCatalogs();
478
479 // Add all children nested catalogs
480
2/2
✓ Branch 1 taken 196 times.
✓ Branch 2 taken 98 times.
294 for (unsigned i = 0; i < children.size(); i++) {
481
2/4
✓ Branch 1 taken 196 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 196 times.
✗ Branch 5 not taken.
196 result_list->push_back(children.at(i).mountpoint);
482 }
483
484 98 Unlock();
485 98 return true;
486 98 }
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 342 bool AbstractCatalogManager<CatalogT>::Listing(const PathString &path,
527 DirectoryEntryList *listing,
528 const bool expand_symlink) {
529
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 EnforceSqliteMemLimit();
530 bool result;
531 342 ReadLock();
532
533 // Find catalog, possibly load nested
534
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 CatalogT *best_fit = FindCatalog(path);
535 342 CatalogT *catalog = best_fit;
536
3/4
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 53 times.
✓ Branch 4 taken 289 times.
342 if (MountSubtree(path, best_fit, true /* is_listable */, NULL)) {
537
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 StageNestedCatalogAndUnlock(path, best_fit, true /* is_listable */);
538 53 WriteLock();
539 // Check again to avoid race
540
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 best_fit = FindCatalog(path);
541
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 result = MountSubtree(path, best_fit, true /* is_listable */, &catalog);
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
53 if (!result) {
543 Unlock();
544 return false;
545 }
546 }
547
548 342 perf::Inc(statistics_.n_listing);
549
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 result = catalog->ListingPath(path, listing, expand_symlink);
550
551 342 Unlock();
552 342 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 97 bool AbstractCatalogManager<CatalogT>::ListingStat(const PathString &path,
564 StatEntryList *listing) {
565
1/2
✓ Branch 1 taken 97 times.
✗ Branch 2 not taken.
97 EnforceSqliteMemLimit();
566 bool result;
567 97 ReadLock();
568
569 // Find catalog, possibly load nested
570
1/2
✓ Branch 1 taken 97 times.
✗ Branch 2 not taken.
97 CatalogT *best_fit = FindCatalog(path);
571 97 CatalogT *catalog = best_fit;
572
2/4
✓ Branch 1 taken 97 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 97 times.
97 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 97 perf::Inc(statistics_.n_listing);
585
1/2
✓ Branch 1 taken 97 times.
✗ Branch 2 not taken.
97 result = catalog->ListingPathStat(path, listing);
586
587 97 Unlock();
588 97 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 144 catalog::Counters AbstractCatalogManager<CatalogT>::LookupCounters(
631 const PathString &path, std::string *subcatalog_path, shash::Any *hash) {
632
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 EnforceSqliteMemLimit();
633 bool result;
634 144 ReadLock();
635
636 // Look past current path to mount up to intended location
637
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 PathString catalog_path(path);
638
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 catalog_path.Append("/.cvmfscatalog", 14);
639
640 // Find catalog, possibly load nested
641
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 CatalogT *best_fit = FindCatalog(catalog_path);
642 144 CatalogT *catalog = best_fit;
643
2/4
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 144 times.
144 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 144 *hash = catalog->hash();
660
2/4
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 144 times.
✗ Branch 5 not taken.
144 *subcatalog_path = catalog->mountpoint().ToString();
661 144 catalog::Counters counters = catalog->GetCounters();
662 144 Unlock();
663 144 return counters;
664 144 }
665
666
667 template<class CatalogT>
668 538 uint64_t AbstractCatalogManager<CatalogT>::GetRevision() const {
669 538 ReadLock();
670 538 const uint64_t revision = GetRevisionNoLock();
671 538 Unlock();
672
673 538 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 1027 uint64_t AbstractCatalogManager<CatalogT>::GetRevisionNoLock() const {
683 1027 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 102 uint64_t AbstractCatalogManager<CatalogT>::GetTimestampNoLock() const {
702 102 return timestamp_cache_;
703 }
704
705 template<class CatalogT>
706 506 bool AbstractCatalogManager<CatalogT>::GetVOMSAuthz(std::string *authz) const {
707 506 ReadLock();
708 506 const bool has_authz = has_authz_cache_;
709
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 506 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
506 if (has_authz && authz)
710 *authz = authz_cache_;
711 506 Unlock();
712 506 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 6 uint64_t AbstractCatalogManager<CatalogT>::GetTTL() const {
727 6 ReadLock();
728 6 const uint64_t ttl = GetRootCatalog()->GetTTL();
729 6 Unlock();
730 6 return ttl;
731 }
732
733
734 template<class CatalogT>
735 180 int AbstractCatalogManager<CatalogT>::GetNumCatalogs() const {
736 180 ReadLock();
737 180 int const result = catalogs_.size();
738 180 Unlock();
739 180 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 3249 InodeRange AbstractCatalogManager<CatalogT>::AcquireInodes(uint64_t size) {
760 3249 InodeRange result;
761 3249 result.offset = inode_gauge_;
762 3249 result.size = size;
763
764 3249 inode_gauge_ += size;
765 3249 LogCvmfs(kLogCatalog, kLogDebug, "allocating inodes from %lu to %lu.",
766
1/2
✓ Branch 1 taken 3249 times.
✗ Branch 2 not taken.
3249 result.offset + 1, inode_gauge_);
767
768 3249 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 3244 void AbstractCatalogManager<CatalogT>::ReleaseInodes(const InodeRange chunk) {
779 // TODO(jblomer) currently inodes are only released on remount
780 3244 }
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 16056 CatalogT *AbstractCatalogManager<CatalogT>::FindCatalog(
791 const PathString &path) const {
792
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16056 times.
16056 assert(catalogs_.size() > 0);
793
794 // Start at the root catalog and successively go down the catalog tree
795 16056 CatalogT *best_fit = GetRootCatalog();
796 16056 CatalogT *next_fit = NULL;
797
5/5
✓ Branch 2 taken 391 times.
✓ Branch 3 taken 16778 times.
✓ Branch 4 taken 1974 times.
✓ Branch 5 taken 347 times.
✓ Branch 6 taken 44 times.
19143 while (best_fit->mountpoint() != path) {
798 17125 next_fit = best_fit->FindSubtree(path);
799
2/2
✓ Branch 0 taken 14038 times.
✓ Branch 1 taken 3087 times.
17125 if (next_fit == NULL)
800 14038 break;
801 3087 best_fit = next_fit;
802 }
803
804 16056 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 2192 bool AbstractCatalogManager<CatalogT>::IsAttached(
816 const PathString &root_path, CatalogT **attached_catalog) const {
817
2/2
✓ Branch 1 taken 1593 times.
✓ Branch 2 taken 599 times.
2192 if (catalogs_.size() == 0)
818 1593 return false;
819
820 599 CatalogT *best_fit = FindCatalog(root_path);
821
4/5
✓ Branch 2 taken 114 times.
✓ Branch 3 taken 485 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 92 times.
✓ Branch 6 taken 22 times.
599 if (best_fit->mountpoint() != root_path)
822 577 return false;
823
824
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (attached_catalog != NULL)
825 22 *attached_catalog = best_fit;
826 22 return true;
827 }
828
829
830 template<class CatalogT>
831 454 void AbstractCatalogManager<CatalogT>::StageNestedCatalogAndUnlock(
832 const PathString &path, const CatalogT *parent, bool is_listable) {
833
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 454 times.
454 assert(parent);
834 454 const unsigned path_len = path.GetLength();
835
836 454 perf::Inc(statistics_.n_nested_listing);
837 typedef typename CatalogT::NestedCatalogList NestedCatalogList;
838 454 const NestedCatalogList &nested_catalogs = parent->ListNestedCatalogs();
839
840 908 for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(),
841 454 iEnd = nested_catalogs.end();
842
1/2
✓ Branch 1 taken 551 times.
✗ Branch 2 not taken.
551 i != iEnd;
843 97 ++i) {
844
2/2
✓ Branch 2 taken 97 times.
✓ Branch 3 taken 454 times.
551 if (!path.StartsWith(i->mountpoint))
845 97 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 454 const unsigned mountpoint_len = i->mountpoint.GetLength();
851
4/6
✓ Branch 0 taken 305 times.
✓ Branch 1 taken 149 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 305 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 454 times.
454 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 401 times.
✓ Branch 1 taken 53 times.
✓ Branch 2 taken 123 times.
✓ Branch 3 taken 278 times.
454 if (!is_listable && (path_len == mountpoint_len))
856 123 break;
857
858 331 Unlock();
859
2/4
✓ Branch 1 taken 331 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 331 times.
✗ Branch 7 not taken.
662 LogCvmfs(kLogCatalog, kLogDebug, "staging nested catalog at %s (%s)",
860 662 i->mountpoint.c_str(), i->hash.ToString().c_str());
861
1/2
✓ Branch 3 taken 331 times.
✗ Branch 4 not taken.
331 StageNestedCatalogByHash(i->hash, i->mountpoint);
862 331 return;
863 }
864 123 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 14496 bool AbstractCatalogManager<CatalogT>::MountSubtree(const PathString &path,
878 const CatalogT *entry_point,
879 bool is_listable,
880 CatalogT **leaf_catalog) {
881 14496 bool result = true;
882 14496 CatalogT *parent = (entry_point == NULL)
883
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14496 times.
14496 ? GetRootCatalog()
884 : const_cast<CatalogT *>(entry_point);
885
2/5
✓ Branch 1 taken 14496 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14496 times.
14496 assert(path.StartsWith(parent->mountpoint()));
886
887 14496 const unsigned path_len = path.GetLength();
888
889 // Try to find path as a super string of nested catalog mount points
890 14496 perf::Inc(statistics_.n_nested_listing);
891 typedef typename CatalogT::NestedCatalogList NestedCatalogList;
892
1/2
✓ Branch 1 taken 14190 times.
✗ Branch 2 not taken.
14496 const NestedCatalogList &nested_catalogs = parent->ListNestedCatalogs();
893 28992 for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(),
894 14496 iEnd = nested_catalogs.end();
895
2/2
✓ Branch 1 taken 3637 times.
✓ Branch 2 taken 13346 times.
16983 i != iEnd;
896 2487 ++i) {
897 // Next nesting level
898
2/2
✓ Branch 2 taken 1253 times.
✓ Branch 3 taken 2384 times.
3637 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 1253 unsigned const mountpoint_len = i->mountpoint.GetLength();
903
6/6
✓ Branch 0 taken 1005 times.
✓ Branch 1 taken 248 times.
✓ Branch 3 taken 103 times.
✓ Branch 4 taken 902 times.
✓ Branch 5 taken 103 times.
✓ Branch 6 taken 1150 times.
1253 if (path_len > mountpoint_len && path.GetChars()[mountpoint_len] != '/')
904 103 continue;
905
906 // Found a nested catalog transition point
907
4/4
✓ Branch 0 taken 940 times.
✓ Branch 1 taken 210 times.
✓ Branch 2 taken 97 times.
✓ Branch 3 taken 843 times.
1150 if (!is_listable && (path_len == mountpoint_len))
908 97 break;
909
910
2/2
✓ Branch 0 taken 454 times.
✓ Branch 1 taken 599 times.
1053 if (leaf_catalog == NULL)
911 454 return true;
912 CatalogT *new_nested;
913
1/2
✓ Branch 2 taken 599 times.
✗ Branch 3 not taken.
599 LogCvmfs(kLogCatalog, kLogDebug, "load nested catalog at %s",
914 599 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 599 times.
599 if (i->hash.IsNull())
918 return false;
919
1/2
✓ Branch 3 taken 599 times.
✗ Branch 4 not taken.
599 new_nested = MountCatalog(i->mountpoint, i->hash, parent);
920
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 599 times.
599 if (!new_nested)
921 return false;
922
923
1/2
✓ Branch 1 taken 599 times.
✗ Branch 2 not taken.
599 result = MountSubtree(path, new_nested, is_listable, &parent);
924 599 break;
925 }
926 }
927
928
2/2
✓ Branch 0 taken 1067 times.
✓ Branch 1 taken 12975 times.
14042 if (leaf_catalog == NULL)
929 1067 return false;
930 12975 *leaf_catalog = parent;
931 12975 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 2192 CatalogT *AbstractCatalogManager<CatalogT>::MountCatalog(
941 const PathString &mountpoint,
942 const shash::Any &hash,
943 CatalogT *parent_catalog) {
944 2192 CatalogT *attached_catalog = NULL;
945
3/4
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 2170 times.
2192 if (IsAttached(mountpoint, &attached_catalog)) {
946 22 return attached_catalog;
947 }
948
949
1/2
✓ Branch 1 taken 2170 times.
✗ Branch 2 not taken.
2170 CatalogContext ctlg_context(hash, mountpoint, kCtlgLocationMounted);
950
951
7/9
✓ Branch 1 taken 2170 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1593 times.
✓ Branch 4 taken 577 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1257 times.
✓ Branch 7 taken 336 times.
✓ Branch 8 taken 1257 times.
✓ Branch 9 taken 913 times.
2170 if (ctlg_context.IsRootCatalog() && hash.IsNull()) {
952
3/4
✓ Branch 1 taken 1257 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 112 times.
✓ Branch 4 taken 1145 times.
1257 if (GetNewRootCatalogContext(&ctlg_context) == kLoadFail) {
953
1/2
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
112 LogCvmfs(kLogCatalog, kLogDebug,
954 "failed to retrieve valid root catalog '%s'",
955 mountpoint.c_str());
956 112 return NULL;
957 }
958 }
959
960
1/2
✓ Branch 1 taken 2058 times.
✗ Branch 2 not taken.
2058 const LoadReturn retval = LoadCatalogByHash(&ctlg_context);
961
2/4
✓ Branch 0 taken 2058 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2058 times.
2058 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 2058 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2058 times.
✗ Branch 5 not taken.
2058 attached_catalog = CreateCatalog(ctlg_context.mountpoint(),
968 2058 ctlg_context.hash(), parent_catalog);
969
970 // Attach loaded catalog
971
3/7
✓ Branch 1 taken 2058 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2058 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 2058 times.
2058 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 212 times.
✓ Branch 1 taken 1846 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 194 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 2040 times.
2058 if ((catalog_watermark_ > 0) && (catalogs_.size() >= catalog_watermark_)) {
979
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 DetachSiblings(mountpoint);
980 }
981
982 2058 return attached_catalog;
983 2170 }
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 294 CatalogT *AbstractCatalogManager<CatalogT>::LoadFreeCatalog(
992 const PathString &mountpoint, const shash::Any &hash) {
993
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 294 times.
294 assert(!hash.IsNull());
994
1/2
✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
294 CatalogContext ctlg_context(hash, mountpoint, kCtlgNoLocationNeeded);
995
996
2/2
✓ Branch 1 taken 268 times.
✓ Branch 2 taken 26 times.
294 const LoadReturn load_ret = LoadCatalogByHash(&ctlg_context);
997
998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 268 times.
268 if (load_ret != kLoadNew) {
999 return NULL;
1000 }
1001
1002
3/6
✓ Branch 1 taken 268 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 268 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 268 times.
✗ Branch 8 not taken.
268 CatalogT *catalog = CatalogT::AttachFreely(
1003 268 mountpoint.ToString(), ctlg_context.sqlite_path(), ctlg_context.hash());
1004
1/2
✓ Branch 1 taken 268 times.
✗ Branch 2 not taken.
268 catalog->TakeDatabaseFileOwnership();
1005 268 return catalog;
1006 294 }
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 3249 bool AbstractCatalogManager<CatalogT>::AttachCatalog(const string &db_path,
1017 CatalogT *new_catalog) {
1018
1/2
✓ Branch 2 taken 3249 times.
✗ Branch 3 not taken.
3249 LogCvmfs(kLogCatalog, kLogDebug, "attaching catalog file %s",
1019 db_path.c_str());
1020
1021 // Initialize the new catalog
1022
2/4
✓ Branch 1 taken 3249 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3249 times.
3249 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 3249 uint64_t const inode_chunk_size = new_catalog->max_row_id();
1030
1/2
✓ Branch 1 taken 3249 times.
✗ Branch 2 not taken.
3249 InodeRange const range = AcquireInodes(inode_chunk_size);
1031 3249 new_catalog->set_inode_range(range);
1032
1/2
✓ Branch 1 taken 3097 times.
✗ Branch 2 not taken.
3249 new_catalog->SetInodeAnnotation(inode_annotation_);
1033
1/2
✓ Branch 1 taken 3097 times.
✗ Branch 2 not taken.
3249 new_catalog->SetOwnerMaps(&uid_map_, &gid_map_);
1034
1035 // Add catalog to the manager
1036
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3249 times.
3249 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 3249 times.
✗ Branch 2 not taken.
3249 CheckInodeWatermark();
1043
1044 // The revision of the catalog tree is given by the root catalog revision
1045
2/2
✓ Branch 1 taken 1483 times.
✓ Branch 2 taken 1766 times.
3249 if (catalogs_.empty()) {
1046
1/2
✓ Branch 1 taken 1423 times.
✗ Branch 2 not taken.
1483 revision_cache_ = new_catalog->GetRevision();
1047
1/2
✓ Branch 1 taken 1423 times.
✗ Branch 2 not taken.
1483 timestamp_cache_ = new_catalog->GetLastModified();
1048 1483 statistics_.catalog_revision->Set(revision_cache_);
1049
1/2
✓ Branch 1 taken 1423 times.
✗ Branch 2 not taken.
1483 has_authz_cache_ = new_catalog->GetVOMSAuthz(&authz_cache_);
1050 1483 volatile_flag_ = new_catalog->volatile_flag();
1051 }
1052
1053
1/2
✓ Branch 1 taken 3249 times.
✗ Branch 2 not taken.
3249 catalogs_.push_back(new_catalog);
1054
1/2
✓ Branch 1 taken 3249 times.
✗ Branch 2 not taken.
3249 ActivateCatalog(new_catalog);
1055 3249 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 3244 void AbstractCatalogManager<CatalogT>::DetachCatalog(CatalogT *catalog) {
1069
2/2
✓ Branch 1 taken 1708 times.
✓ Branch 2 taken 1536 times.
3244 if (catalog->HasParent())
1070
1/2
✓ Branch 2 taken 1708 times.
✗ Branch 3 not taken.
1708 catalog->parent()->RemoveChild(catalog);
1071
1072 3244 ReleaseInodes(catalog->inode_range());
1073
1/2
✓ Branch 1 taken 3244 times.
✗ Branch 2 not taken.
3244 UnloadCatalog(catalog);
1074
1075 // Delete catalog from internal lists
1076 3244 typename CatalogList::iterator i;
1077 3244 typename CatalogList::const_iterator iend;
1078
1/2
✓ Branch 5 taken 6058 times.
✗ Branch 6 not taken.
6058 for (i = catalogs_.begin(), iend = catalogs_.end(); i != iend; ++i) {
1079
2/2
✓ Branch 1 taken 3244 times.
✓ Branch 2 taken 2814 times.
6058 if (*i == catalog) {
1080
1/2
✓ Branch 2 taken 3244 times.
✗ Branch 3 not taken.
3244 catalogs_.erase(i);
1081
1/2
✓ Branch 0 taken 3244 times.
✗ Branch 1 not taken.
3244 delete catalog;
1082 3244 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 3189 void AbstractCatalogManager<CatalogT>::DetachSubtree(CatalogT *catalog) {
1098 // Detach all child catalogs recursively
1099 3189 typename CatalogList::const_iterator i;
1100 3189 typename CatalogList::const_iterator iend;
1101
1/2
✓ Branch 1 taken 3189 times.
✗ Branch 2 not taken.
3189 CatalogList catalogs_to_detach = catalog->GetChildren();
1102 3189 for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end();
1103
2/2
✓ Branch 2 taken 1596 times.
✓ Branch 3 taken 3189 times.
4785 i != iend; ++i) {
1104
1/2
✓ Branch 2 taken 1596 times.
✗ Branch 3 not taken.
1596 DetachSubtree(*i);
1105 }
1106
1107
1/2
✓ Branch 1 taken 3189 times.
✗ Branch 2 not taken.
3189 DetachCatalog(catalog);
1108 3189 }
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 24 void AbstractCatalogManager<CatalogT>::DetachSiblings(
1117 const PathString &current_tree) {
1118 bool again;
1119
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
24 do {
1120 24 again = false;
1121 24 unsigned const N = catalogs_.size();
1122
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 18 times.
72 for (unsigned i = 0; i < N; ++i) {
1123
5/9
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 54 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 54 times.
✗ Branch 9 not taken.
✓ Branch 13 taken 6 times.
✓ Branch 14 taken 48 times.
54 if (!HasPrefix(current_tree.ToString(),
1124 54 catalogs_[i]->mountpoint().ToString(),
1125 false /* ignore_case */)) {
1126 6 DetachSubtree(catalogs_[i]);
1127 6 again = true;
1128 6 break;
1129 }
1130 }
1131 } while (again);
1132 18 perf::Inc(statistics_.n_detach_siblings);
1133 18 }
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 1487 void AbstractCatalogManager<CatalogT>::EnforceSqliteMemLimit() {
1194 char *mem_enforced = static_cast<char *>(
1195 1487 pthread_getspecific(pkey_sqlitemem_));
1196
2/2
✓ Branch 0 taken 337 times.
✓ Branch 1 taken 1150 times.
1487 if (mem_enforced == NULL) {
1197 337 sqlite3_soft_heap_limit(kSqliteMemPerThread);
1198 337 pthread_setspecific(pkey_sqlitemem_, this);
1199 }
1200 1487 }
1201
1202 } // namespace catalog
1203
1204
1205 #endif // CVMFS_CATALOG_MGR_IMPL_H_
1206