GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_mgr_impl.h
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 419 566 74.0%
Branches: 295 654 45.1%

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