GCC Code Coverage Report


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