GCC Code Coverage Report


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