GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_mgr_impl.h
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 426 575 74.1%
Branches: 295 654 45.1%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System
3 */
4
5 // avoid clang-tidy false positives (at least starting with clang14)
6 // NOLINTBEGIN
7
8 #ifndef CVMFS_CATALOG_MGR_IMPL_H_
9 #define CVMFS_CATALOG_MGR_IMPL_H_
10
11 #ifndef __STDC_FORMAT_MACROS
12 #define __STDC_FORMAT_MACROS
13 #endif
14
15
16 #include <cassert>
17 #include <string>
18 #include <vector>
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 3056 AbstractCatalogManager<CatalogT>::AbstractCatalogManager(
31 perf::Statistics *statistics)
32
1/2
✓ Branch 4 taken 3056 times.
✗ Branch 5 not taken.
3056 : statistics_(statistics) {
33 3056 inode_watermark_status_ = 0;
34 3056 inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset;
35 3056 revision_cache_ = 0;
36 3056 timestamp_cache_ = 0;
37 3056 catalog_watermark_ = 0;
38 3056 volatile_flag_ = false;
39 3056 has_authz_cache_ = false;
40 3056 inode_annotation_ = NULL;
41 3056 incarnation_ = 0;
42 3056 rwlock_ = reinterpret_cast<pthread_rwlock_t *>(
43 3056 smalloc(sizeof(pthread_rwlock_t)));
44 3056 int retval = pthread_rwlock_init(rwlock_, NULL);
45
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3056 times.
3056 assert(retval == 0);
46 3056 retval = pthread_key_create(&pkey_sqlitemem_, NULL);
47
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3056 times.
3056 assert(retval == 0);
48 3056 }
49
50 template<class CatalogT>
51 6108 AbstractCatalogManager<CatalogT>::~AbstractCatalogManager() {
52 6108 DetachAll();
53 6108 pthread_key_delete(pkey_sqlitemem_);
54 6108 pthread_rwlock_destroy(rwlock_);
55 6108 free(rwlock_);
56 }
57
58 template<class CatalogT>
59 1133 void AbstractCatalogManager<CatalogT>::SetInodeAnnotation(
60 InodeAnnotation *new_annotation) {
61
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1133 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1133 assert(catalogs_.empty() || (new_annotation == inode_annotation_));
62 1133 inode_annotation_ = new_annotation;
63 1133 }
64
65 template<class CatalogT>
66 1461 void AbstractCatalogManager<CatalogT>::SetOwnerMaps(const OwnerMap &uid_map,
67 const OwnerMap &gid_map) {
68 1461 uid_map_ = uid_map;
69 1461 gid_map_ = gid_map;
70 1461 }
71
72 template<class CatalogT>
73 1080 void AbstractCatalogManager<CatalogT>::SetCatalogWatermark(unsigned limit) {
74 1080 catalog_watermark_ = limit;
75 1080 }
76
77 template<class CatalogT>
78 4282 void AbstractCatalogManager<CatalogT>::CheckInodeWatermark() {
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4282 times.
4282 if (inode_watermark_status_ > 0)
80 return;
81
82 4282 uint64_t highest_inode = inode_gauge_;
83
2/2
✓ Branch 0 taken 839 times.
✓ Branch 1 taken 3443 times.
4282 if (inode_annotation_)
84 839 highest_inode += inode_annotation_->GetGeneration();
85 4282 uint64_t uint32_border = 1;
86 4282 uint32_border = uint32_border << 32;
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4282 times.
4282 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 2179 bool AbstractCatalogManager<CatalogT>::Init() {
100 2179 LogCvmfs(kLogCatalog, kLogDebug, "Initialize catalog");
101 2179 WriteLock();
102
2/4
✓ Branch 2 taken 2179 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2179 times.
✗ Branch 6 not taken.
2179 bool attached = MountCatalog(PathString("", 0), shash::Any(), NULL);
103 2179 Unlock();
104
105
2/2
✓ Branch 0 taken 243 times.
✓ Branch 1 taken 1936 times.
2179 if (!attached) {
106 243 LogCvmfs(kLogCatalog, kLogDebug, "failed to initialize root catalog");
107 }
108
109 2179 return attached;
110 }
111
112
113 /**
114 * Remounts the root catalog if necessary. If a newer root catalog exists,
115 * it is mounted and replaces the currently mounted tree (all existing catalogs
116 * are detached)
117 */
118 template<class CatalogT>
119 11 LoadReturn AbstractCatalogManager<CatalogT>::RemountDryrun() {
120
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 LogCvmfs(kLogCatalog, kLogDebug, "dryrun remounting repositories");
121
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 CatalogContext ctlg_context;
122
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
22 return GetNewRootCatalogContext(&ctlg_context);
123 11 }
124
125 template<class CatalogT>
126 11 LoadReturn AbstractCatalogManager<CatalogT>::Remount() {
127
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 LogCvmfs(kLogCatalog, kLogDebug, "remounting repositories");
128
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 CatalogContext ctlg_context;
129
130
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 if (GetNewRootCatalogContext(&ctlg_context) != kLoadNew
131
5/8
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 9 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 11 times.
11 && GetNewRootCatalogContext(&ctlg_context) != kLoadUp2Date) {
132 LogCvmfs(kLogCatalog, kLogDebug,
133 "remounting repositories: "
134 "Did not find any valid root catalog to mount");
135 return kLoadFail;
136 }
137
138 11 WriteLock();
139
140
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 const LoadReturn load_error = LoadCatalogByHash(&ctlg_context);
141
142
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9 times.
11 if (load_error == kLoadNew) {
143 2 inode_t 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 11 times.
✗ Branch 2 not taken.
11 CheckInodeWatermark();
158 11 Unlock();
159
160 11 return load_error;
161 11 }
162
163 /**
164 * Remounts to the given hash
165 */
166 template<class CatalogT>
167 LoadReturn AbstractCatalogManager<CatalogT>::ChangeRoot(
168 const shash::Any &root_hash) {
169 assert(!root_hash.IsNull());
170 LogCvmfs(kLogCatalog, kLogDebug, "switching to root hash %s",
171 root_hash.ToString().c_str());
172
173 WriteLock();
174
175 CatalogContext ctlg_context(root_hash, PathString("", 0),
176 kCtlgNoLocationNeeded);
177 // we do not need to set revision as LoadCatalogByHash
178 // needs only mountpoint, hash
179
180 const LoadReturn load_error = LoadCatalogByHash(&ctlg_context);
181
182 if (load_error == kLoadNew) {
183 inode_t 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 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 73 void AbstractCatalogManager<CatalogT>::DetachNested() {
209 73 WriteLock();
210
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 73 times.
73 if (catalogs_.empty()) {
211 Unlock();
212 return;
213 }
214
215 73 typename CatalogList::const_iterator i;
216 73 typename CatalogList::const_iterator iend;
217
1/2
✓ Branch 2 taken 73 times.
✗ Branch 3 not taken.
73 CatalogList catalogs_to_detach = GetRootCatalog()->GetChildren();
218 73 for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end();
219
2/2
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 73 times.
131 i != iend; ++i) {
220
1/2
✓ Branch 2 taken 58 times.
✗ Branch 3 not taken.
58 DetachSubtree(*i);
221 }
222
223 73 Unlock();
224 73 }
225
226
227 /**
228 * Returns the NULL hash if the nested catalog is not found.
229 */
230 template<class CatalogT>
231 269 shash::Any AbstractCatalogManager<CatalogT>::GetNestedCatalogHash(
232 const PathString &mountpoint) {
233
2/4
✓ Branch 1 taken 269 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 269 times.
269 assert(!mountpoint.IsEmpty());
234
1/2
✓ Branch 1 taken 269 times.
✗ Branch 2 not taken.
269 CatalogT *catalog = FindCatalog(mountpoint);
235
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 269 times.
269 assert(catalog != NULL);
236
3/4
✓ Branch 1 taken 269 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 260 times.
269 if (catalog->mountpoint() == mountpoint) {
237 9 catalog = catalog->parent();
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 assert(catalog != NULL);
239 }
240
1/2
✓ Branch 1 taken 269 times.
✗ Branch 2 not taken.
269 shash::Any result;
241 uint64_t size;
242
1/2
✓ Branch 1 taken 269 times.
✗ Branch 2 not taken.
269 catalog->FindNested(mountpoint, &result, &size);
243 538 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 1510 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 1510 times.
1510 assert(dirent);
262
2/4
✓ Branch 1 taken 1510 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1510 times.
✗ Branch 5 not taken.
1510 *dirent = DirectoryEntry();
263
264 // create a dummy negative directory entry
265
1/2
✓ Branch 1 taken 1510 times.
✗ Branch 2 not taken.
1510 const DirectoryEntry dirent_negative = DirectoryEntry(
266 catalog::kDirentNegative);
267
268
1/2
✓ Branch 1 taken 1510 times.
✗ Branch 2 not taken.
1510 EnforceSqliteMemLimit();
269 1510 ReadLock();
270
271
1/2
✓ Branch 1 taken 1510 times.
✗ Branch 2 not taken.
1510 CatalogT *best_fit = FindCatalog(path);
272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1510 times.
1510 assert(best_fit != NULL);
273
274 1510 perf::Inc(statistics_.n_lookup_path);
275
2/4
✓ Branch 1 taken 1510 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 1510 times.
✗ Branch 7 not taken.
1510 LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in catalog: '%s'",
276 path.c_str(), best_fit->mountpoint().c_str());
277
1/2
✓ Branch 1 taken 1510 times.
✗ Branch 2 not taken.
1510 bool found = best_fit->LookupPath(path, dirent);
278
279 // Possibly in a nested catalog
280
7/8
✓ Branch 0 taken 394 times.
✓ Branch 1 taken 1116 times.
✓ Branch 3 taken 394 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 231 times.
✓ Branch 6 taken 163 times.
✓ Branch 7 taken 231 times.
✓ Branch 8 taken 1279 times.
1510 if (!found && MountSubtree(path, best_fit, false /* is_listable */, NULL)) {
281
1/2
✓ Branch 2 taken 231 times.
✗ Branch 3 not taken.
231 LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in a nested catalog",
282 path.c_str());
283
1/2
✓ Branch 1 taken 231 times.
✗ Branch 2 not taken.
231 StageNestedCatalogAndUnlock(path, best_fit, false /* is_listable */);
284 231 WriteLock();
285 // Check again to avoid race
286
1/2
✓ Branch 1 taken 231 times.
✗ Branch 2 not taken.
231 best_fit = FindCatalog(path);
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 assert(best_fit != NULL);
288 231 perf::Inc(statistics_.n_lookup_path);
289
1/2
✓ Branch 1 taken 231 times.
✗ Branch 2 not taken.
231 found = best_fit->LookupPath(path, dirent);
290
291
1/2
✓ Branch 0 taken 231 times.
✗ Branch 1 not taken.
231 if (!found) {
292
1/2
✓ Branch 1 taken 231 times.
✗ Branch 2 not taken.
231 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 231 times.
✗ Branch 2 not taken.
231 found = MountSubtree(path, best_fit, false /* is_listable */,
297 &nested_catalog);
298
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 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 231 times.
✗ Branch 1 not taken.
231 if (nested_catalog != best_fit) {
306 231 perf::Inc(statistics_.n_lookup_path);
307
1/2
✓ Branch 1 taken 231 times.
✗ Branch 2 not taken.
231 found = nested_catalog->LookupPath(path, dirent);
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 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 231 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 231 times.
231 assert(found);
326 }
327 // Not in a nested catalog (because no nested cataog fits), ENOENT
328
2/2
✓ Branch 0 taken 163 times.
✓ Branch 1 taken 1347 times.
1510 if (!found) {
329
1/2
✓ Branch 2 taken 163 times.
✗ Branch 3 not taken.
163 LogCvmfs(kLogCatalog, kLogDebug, "ENOENT: '%s'", path.c_str());
330
1/2
✓ Branch 0 taken 163 times.
✗ Branch 1 not taken.
163 if (dirent != NULL)
331
1/2
✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
163 *dirent = dirent_negative;
332 163 goto lookup_path_notfound;
333 }
334
335
2/4
✓ Branch 1 taken 1347 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 1347 times.
✗ Branch 7 not taken.
1347 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 1347 times.
1347 if ((options & kLookupRawSymlink) == kLookupRawSymlink) {
339 LinkString raw_symlink;
340 bool 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 1347 Unlock();
346 1347 return true;
347
348 163 lookup_path_notfound:
349 163 Unlock();
350 // Includes both: ENOENT and not found due to I/O error
351 163 perf::Inc(statistics_.n_lookup_path_negative);
352 163 return false;
353 1510 }
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 280 bool AbstractCatalogManager<CatalogT>::LookupNested(const PathString &path,
370 PathString *mountpoint,
371 shash::Any *hash,
372 uint64_t *size) {
373
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 EnforceSqliteMemLimit();
374 280 bool result = false;
375 280 ReadLock();
376
377 // Look past current path to mount up to intended location
378
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 PathString catalog_path(path);
379
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 catalog_path.Append("/.cvmfscatalog", 14);
380
381 // Find catalog, possibly load nested
382
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 CatalogT *best_fit = FindCatalog(catalog_path);
383 280 CatalogT *catalog = best_fit;
384
3/4
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 79 times.
✓ Branch 4 taken 201 times.
280 if (MountSubtree(catalog_path, best_fit, false /* is_listable */, NULL)) {
385
1/2
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
79 StageNestedCatalogAndUnlock(path, best_fit, false);
386 79 WriteLock();
387 // Check again to avoid race
388
1/2
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
79 best_fit = FindCatalog(catalog_path);
389
1/2
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
79 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 79 times.
79 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 231 times.
✓ Branch 2 taken 49 times.
280 if (catalog->HasParent()) {
400
2/4
✓ Branch 2 taken 231 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 231 times.
✗ Branch 6 not taken.
231 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 280 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 280 times.
✗ Branch 5 not taken.
280 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 49 times.
✓ Branch 1 taken 231 times.
280 if (!result) {
411 49 *hash = GetRootCatalog()->hash();
412 49 *size = 0;
413 49 result = true;
414 }
415
416 280 Unlock();
417 280 return result;
418 280 }
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 96 bool AbstractCatalogManager<CatalogT>::ListCatalogSkein(
432 const PathString &path, std::vector<PathString> *result_list) {
433
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 EnforceSqliteMemLimit();
434 bool result;
435 96 ReadLock();
436
437 // Look past current path to mount up to intended location
438
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 PathString test(path);
439
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 test.Append("/.cvmfscatalog", 14);
440
441 // Find catalog, possibly load nested
442
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 CatalogT *best_fit = FindCatalog(test);
443 96 CatalogT *catalog = best_fit;
444 // True if there is an available nested catalog
445
3/4
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 48 times.
96 if (MountSubtree(test, best_fit, false /* is_listable */, NULL)) {
446
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 StageNestedCatalogAndUnlock(path, best_fit, false);
447 48 WriteLock();
448 // Check again to avoid race
449
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 best_fit = FindCatalog(test);
450
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 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 48 times.
48 if (!result) {
453 Unlock();
454 return false;
455 }
456 }
457
458 // Build listing
459 96 CatalogT *cur_parent = catalog->parent();
460
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 if (cur_parent) {
461 // Walk up parent tree to find base
462 96 std::vector<catalog::Catalog *> parents;
463
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 96 times.
144 while (cur_parent->HasParent()) {
464
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 parents.push_back(cur_parent);
465 48 cur_parent = cur_parent->parent();
466 }
467
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 parents.push_back(cur_parent);
468
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 96 times.
240 while (!parents.empty()) {
469 // Add to list in order starting at root
470
2/4
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✗ Branch 6 not taken.
144 result_list->push_back(parents.back()->root_prefix());
471 144 parents.pop_back();
472 }
473 96 }
474 // Add the current catalog
475
2/4
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
96 result_list->push_back(catalog->root_prefix());
476
477
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 Catalog::NestedCatalogList children = catalog->ListOwnNestedCatalogs();
478
479 // Add all children nested catalogs
480
2/2
✓ Branch 1 taken 192 times.
✓ Branch 2 taken 96 times.
288 for (unsigned i = 0; i < children.size(); i++) {
481
2/4
✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 192 times.
✗ Branch 5 not taken.
192 result_list->push_back(children.at(i).mountpoint);
482 }
483
484 96 Unlock();
485 96 return true;
486 96 }
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 1472 bool AbstractCatalogManager<CatalogT>::Listing(const PathString &path,
527 DirectoryEntryList *listing,
528 const bool expand_symlink) {
529
1/2
✓ Branch 1 taken 1472 times.
✗ Branch 2 not taken.
1472 EnforceSqliteMemLimit();
530 bool result;
531 1472 ReadLock();
532
533 // Find catalog, possibly load nested
534
1/2
✓ Branch 1 taken 1472 times.
✗ Branch 2 not taken.
1472 CatalogT *best_fit = FindCatalog(path);
535 1472 CatalogT *catalog = best_fit;
536
3/4
✓ Branch 1 taken 1472 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 130 times.
✓ Branch 4 taken 1342 times.
1472 if (MountSubtree(path, best_fit, true /* is_listable */, NULL)) {
537
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 StageNestedCatalogAndUnlock(path, best_fit, true /* is_listable */);
538 130 WriteLock();
539 // Check again to avoid race
540
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 best_fit = FindCatalog(path);
541
1/2
✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
130 result = MountSubtree(path, best_fit, true /* is_listable */, &catalog);
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 if (!result) {
543 Unlock();
544 return false;
545 }
546 }
547
548 1472 perf::Inc(statistics_.n_listing);
549
1/2
✓ Branch 1 taken 1472 times.
✗ Branch 2 not taken.
1472 result = catalog->ListingPath(path, listing, expand_symlink);
550
551 1472 Unlock();
552 1472 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 96 bool AbstractCatalogManager<CatalogT>::ListingStat(const PathString &path,
564 StatEntryList *listing) {
565
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 EnforceSqliteMemLimit();
566 bool result;
567 96 ReadLock();
568
569 // Find catalog, possibly load nested
570
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 CatalogT *best_fit = FindCatalog(path);
571 96 CatalogT *catalog = best_fit;
572
2/4
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 96 times.
96 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 96 perf::Inc(statistics_.n_listing);
585
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 result = catalog->ListingPathStat(path, listing);
586
587 96 Unlock();
588 96 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 147 catalog::Counters AbstractCatalogManager<CatalogT>::LookupCounters(
631 const PathString &path, std::string *subcatalog_path, shash::Any *hash) {
632
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 EnforceSqliteMemLimit();
633 bool result;
634 147 ReadLock();
635
636 // Look past current path to mount up to intended location
637
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 PathString catalog_path(path);
638
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 catalog_path.Append("/.cvmfscatalog", 14);
639
640 // Find catalog, possibly load nested
641
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 CatalogT *best_fit = FindCatalog(catalog_path);
642 147 CatalogT *catalog = best_fit;
643
2/4
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 147 times.
147 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 147 *hash = catalog->hash();
660
2/4
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147 times.
✗ Branch 5 not taken.
147 *subcatalog_path = catalog->mountpoint().ToString();
661 147 catalog::Counters counters = catalog->GetCounters();
662 147 Unlock();
663 147 return counters;
664 147 }
665
666
667 template<class CatalogT>
668 1161 uint64_t AbstractCatalogManager<CatalogT>::GetRevision() const {
669 1161 ReadLock();
670 1161 const uint64_t revision = GetRevisionNoLock();
671 1161 Unlock();
672
673 1161 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 1994 uint64_t AbstractCatalogManager<CatalogT>::GetRevisionNoLock() const {
683 1994 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 1073 bool AbstractCatalogManager<CatalogT>::GetVOMSAuthz(std::string *authz) const {
707 1073 ReadLock();
708 1073 const bool has_authz = has_authz_cache_;
709
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1073 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1073 if (has_authz && authz)
710 *authz = authz_cache_;
711 1073 Unlock();
712 1073 return has_authz;
713 }
714
715
716 template<class CatalogT>
717 bool AbstractCatalogManager<CatalogT>::HasExplicitTTL() const {
718 ReadLock();
719 const bool result = GetRootCatalog()->HasExplicitTTL();
720 Unlock();
721 return result;
722 }
723
724
725 template<class CatalogT>
726 9 uint64_t AbstractCatalogManager<CatalogT>::GetTTL() const {
727 9 ReadLock();
728 9 const uint64_t ttl = GetRootCatalog()->GetTTL();
729 9 Unlock();
730 9 return ttl;
731 }
732
733
734 template<class CatalogT>
735 293 int AbstractCatalogManager<CatalogT>::GetNumCatalogs() const {
736 293 ReadLock();
737 293 int result = catalogs_.size();
738 293 Unlock();
739 293 return result;
740 }
741
742
743 /**
744 * Gets a formatted tree of the currently attached catalogs
745 */
746 template<class CatalogT>
747 string AbstractCatalogManager<CatalogT>::PrintHierarchy() const {
748 ReadLock();
749 string output = PrintHierarchyRecursively(GetRootCatalog(), 0);
750 Unlock();
751 return output;
752 }
753
754
755 /**
756 * Assigns the next free numbers in the 64 bit space
757 */
758 template<class CatalogT>
759 4271 InodeRange AbstractCatalogManager<CatalogT>::AcquireInodes(uint64_t size) {
760 4271 InodeRange result;
761 4271 result.offset = inode_gauge_;
762 4271 result.size = size;
763
764 4271 inode_gauge_ += size;
765 4271 LogCvmfs(kLogCatalog, kLogDebug, "allocating inodes from %lu to %lu.",
766
1/2
✓ Branch 1 taken 4271 times.
✗ Branch 2 not taken.
4271 result.offset + 1, inode_gauge_);
767
768 4271 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 4266 void AbstractCatalogManager<CatalogT>::ReleaseInodes(const InodeRange chunk) {
779 // TODO(jblomer) currently inodes are only released on remount
780 4266 }
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 16248 CatalogT *AbstractCatalogManager<CatalogT>::FindCatalog(
791 const PathString &path) const {
792
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16248 times.
16248 assert(catalogs_.size() > 0);
793
794 // Start at the root catalog and successively go down the catalog tree
795 16248 CatalogT *best_fit = GetRootCatalog();
796 16248 CatalogT *next_fit = NULL;
797
5/5
✓ Branch 2 taken 629 times.
✓ Branch 3 taken 15468 times.
✓ Branch 4 taken 2294 times.
✓ Branch 5 taken 558 times.
✓ Branch 6 taken 71 times.
18391 while (best_fit->mountpoint() != path) {
798 16026 next_fit = best_fit->FindSubtree(path);
799
2/2
✓ Branch 0 taken 13883 times.
✓ Branch 1 taken 2143 times.
16026 if (next_fit == NULL)
800 13883 break;
801 2143 best_fit = next_fit;
802 }
803
804 16248 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 3566 bool AbstractCatalogManager<CatalogT>::IsAttached(
816 const PathString &root_path, CatalogT **attached_catalog) const {
817
2/2
✓ Branch 1 taken 2909 times.
✓ Branch 2 taken 657 times.
3566 if (catalogs_.size() == 0)
818 2909 return false;
819
820 657 CatalogT *best_fit = FindCatalog(root_path);
821
4/5
✓ Branch 2 taken 186 times.
✓ Branch 3 taken 471 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 151 times.
✓ Branch 6 taken 35 times.
657 if (best_fit->mountpoint() != root_path)
822 622 return false;
823
824
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 if (attached_catalog != NULL)
825 35 *attached_catalog = best_fit;
826 35 return true;
827 }
828
829
830 template<class CatalogT>
831 488 void AbstractCatalogManager<CatalogT>::StageNestedCatalogAndUnlock(
832 const PathString &path, const CatalogT *parent, bool is_listable) {
833
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 488 times.
488 assert(parent);
834 488 const unsigned path_len = path.GetLength();
835
836 488 perf::Inc(statistics_.n_nested_listing);
837 typedef typename CatalogT::NestedCatalogList NestedCatalogList;
838 488 const NestedCatalogList &nested_catalogs = parent->ListNestedCatalogs();
839
840 976 for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(),
841 488 iEnd = nested_catalogs.end();
842
1/2
✓ Branch 1 taken 557 times.
✗ Branch 2 not taken.
557 i != iEnd;
843 69 ++i) {
844
2/2
✓ Branch 2 taken 69 times.
✓ Branch 3 taken 488 times.
557 if (!path.StartsWith(i->mountpoint))
845 69 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 488 const unsigned mountpoint_len = i->mountpoint.GetLength();
851
4/6
✓ Branch 0 taken 291 times.
✓ Branch 1 taken 197 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 291 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 488 times.
488 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 358 times.
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 112 times.
✓ Branch 3 taken 246 times.
488 if (!is_listable && (path_len == mountpoint_len))
856 112 break;
857
858 376 Unlock();
859
2/4
✓ Branch 1 taken 376 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 376 times.
✗ Branch 7 not taken.
752 LogCvmfs(kLogCatalog, kLogDebug, "staging nested catalog at %s (%s)",
860 752 i->mountpoint.c_str(), i->hash.ToString().c_str());
861
1/2
✓ Branch 3 taken 376 times.
✗ Branch 4 not taken.
376 StageNestedCatalogByHash(i->hash, i->mountpoint);
862 376 return;
863 }
864 112 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 14863 bool AbstractCatalogManager<CatalogT>::MountSubtree(const PathString &path,
878 const CatalogT *entry_point,
879 bool is_listable,
880 CatalogT **leaf_catalog) {
881 14863 bool result = true;
882 14863 CatalogT *parent = (entry_point == NULL)
883
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14863 times.
14863 ? GetRootCatalog()
884 : const_cast<CatalogT *>(entry_point);
885
2/5
✓ Branch 1 taken 14863 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14863 times.
14863 assert(path.StartsWith(parent->mountpoint()));
886
887 14863 unsigned path_len = path.GetLength();
888
889 // Try to find path as a super string of nested catalog mount points
890 14863 perf::Inc(statistics_.n_nested_listing);
891 typedef typename CatalogT::NestedCatalogList NestedCatalogList;
892
1/2
✓ Branch 1 taken 14367 times.
✗ Branch 2 not taken.
14863 const NestedCatalogList &nested_catalogs = parent->ListNestedCatalogs();
893 29726 for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(),
894 14863 iEnd = nested_catalogs.end();
895
2/2
✓ Branch 1 taken 3246 times.
✓ Branch 2 taken 13621 times.
16867 i != iEnd;
896 2004 ++i) {
897 // Next nesting level
898
2/2
✓ Branch 2 taken 1346 times.
✓ Branch 3 taken 1900 times.
3246 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 1346 unsigned mountpoint_len = i->mountpoint.GetLength();
903
6/6
✓ Branch 0 taken 960 times.
✓ Branch 1 taken 386 times.
✓ Branch 3 taken 104 times.
✓ Branch 4 taken 856 times.
✓ Branch 5 taken 104 times.
✓ Branch 6 taken 1242 times.
1346 if (path_len > mountpoint_len && path.GetChars()[mountpoint_len] != '/')
904 104 continue;
905
906 // Found a nested catalog transition point
907
4/4
✓ Branch 0 taken 854 times.
✓ Branch 1 taken 388 times.
✓ Branch 2 taken 97 times.
✓ Branch 3 taken 757 times.
1242 if (!is_listable && (path_len == mountpoint_len))
908 97 break;
909
910
2/2
✓ Branch 0 taken 488 times.
✓ Branch 1 taken 657 times.
1145 if (leaf_catalog == NULL)
911 488 return true;
912 CatalogT *new_nested;
913
1/2
✓ Branch 2 taken 657 times.
✗ Branch 3 not taken.
657 LogCvmfs(kLogCatalog, kLogDebug, "load nested catalog at %s",
914 657 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 657 times.
657 if (i->hash.IsNull())
918 return false;
919
1/2
✓ Branch 3 taken 657 times.
✗ Branch 4 not taken.
657 new_nested = MountCatalog(i->mountpoint, i->hash, parent);
920
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 657 times.
657 if (!new_nested)
921 return false;
922
923
1/2
✓ Branch 1 taken 657 times.
✗ Branch 2 not taken.
657 result = MountSubtree(path, new_nested, is_listable, &parent);
924 657 break;
925 }
926 }
927
928
2/2
✓ Branch 0 taken 1997 times.
✓ Branch 1 taken 12378 times.
14375 if (leaf_catalog == NULL)
929 1997 return false;
930 12378 *leaf_catalog = parent;
931 12378 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 3566 CatalogT *AbstractCatalogManager<CatalogT>::MountCatalog(
941 const PathString &mountpoint,
942 const shash::Any &hash,
943 CatalogT *parent_catalog) {
944 3566 CatalogT *attached_catalog = NULL;
945
3/4
✓ Branch 1 taken 3566 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35 times.
✓ Branch 4 taken 3531 times.
3566 if (IsAttached(mountpoint, &attached_catalog)) {
946 35 return attached_catalog;
947 }
948
949
1/2
✓ Branch 1 taken 3531 times.
✗ Branch 2 not taken.
3531 CatalogContext ctlg_context(hash, mountpoint, kCtlgLocationMounted);
950
951
7/9
✓ Branch 1 taken 3531 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2909 times.
✓ Branch 4 taken 622 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2179 times.
✓ Branch 7 taken 730 times.
✓ Branch 8 taken 2179 times.
✓ Branch 9 taken 1352 times.
3531 if (ctlg_context.IsRootCatalog() && hash.IsNull()) {
952
3/4
✓ Branch 1 taken 2179 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 243 times.
✓ Branch 4 taken 1936 times.
2179 if (GetNewRootCatalogContext(&ctlg_context) == kLoadFail) {
953
1/2
✓ Branch 2 taken 243 times.
✗ Branch 3 not taken.
243 LogCvmfs(kLogCatalog, kLogDebug,
954 "failed to retrieve valid root catalog '%s'",
955 mountpoint.c_str());
956 243 return NULL;
957 }
958 }
959
960
1/2
✓ Branch 1 taken 3288 times.
✗ Branch 2 not taken.
3288 const LoadReturn retval = LoadCatalogByHash(&ctlg_context);
961
2/4
✓ Branch 0 taken 3288 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3288 times.
3288 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 3288 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3288 times.
✗ Branch 5 not taken.
3288 attached_catalog = CreateCatalog(
968 3288 ctlg_context.mountpoint(), ctlg_context.hash(), parent_catalog);
969
970 // Attach loaded catalog
971
3/7
✓ Branch 1 taken 3288 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3288 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3288 times.
3288 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 221 times.
✓ Branch 1 taken 3067 times.
✓ Branch 3 taken 27 times.
✓ Branch 4 taken 194 times.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 3261 times.
3288 if ((catalog_watermark_ > 0) && (catalogs_.size() >= catalog_watermark_)) {
979
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 DetachSiblings(mountpoint);
980 }
981
982 3288 return attached_catalog;
983 3531 }
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 159 CatalogT *AbstractCatalogManager<CatalogT>::LoadFreeCatalog(
992 const PathString &mountpoint, const shash::Any &hash) {
993
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 159 times.
159 assert(!hash.IsNull());
994
1/2
✓ Branch 1 taken 159 times.
✗ Branch 2 not taken.
159 CatalogContext ctlg_context(hash, mountpoint, kCtlgNoLocationNeeded);
995
996
2/2
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 15 times.
159 const LoadReturn load_ret = LoadCatalogByHash(&ctlg_context);
997
998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (load_ret != kLoadNew) {
999 return NULL;
1000 }
1001
1002
3/6
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 144 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 144 times.
✗ Branch 8 not taken.
144 CatalogT *catalog = CatalogT::AttachFreely(
1003 144 mountpoint.ToString(), ctlg_context.sqlite_path(), ctlg_context.hash());
1004
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 catalog->TakeDatabaseFileOwnership();
1005 144 return catalog;
1006 159 }
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 4271 bool AbstractCatalogManager<CatalogT>::AttachCatalog(const string &db_path,
1017 CatalogT *new_catalog) {
1018
1/2
✓ Branch 2 taken 4271 times.
✗ Branch 3 not taken.
4271 LogCvmfs(kLogCatalog, kLogDebug, "attaching catalog file %s",
1019 db_path.c_str());
1020
1021 // Initialize the new catalog
1022
2/4
✓ Branch 1 taken 4271 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4271 times.
4271 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 4271 uint64_t inode_chunk_size = new_catalog->max_row_id();
1030
1/2
✓ Branch 1 taken 4271 times.
✗ Branch 2 not taken.
4271 InodeRange range = AcquireInodes(inode_chunk_size);
1031 4271 new_catalog->set_inode_range(range);
1032
1/2
✓ Branch 1 taken 4022 times.
✗ Branch 2 not taken.
4271 new_catalog->SetInodeAnnotation(inode_annotation_);
1033
1/2
✓ Branch 1 taken 4022 times.
✗ Branch 2 not taken.
4271 new_catalog->SetOwnerMaps(&uid_map_, &gid_map_);
1034
1035 // Add catalog to the manager
1036
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4271 times.
4271 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 4271 times.
✗ Branch 2 not taken.
4271 CheckInodeWatermark();
1043
1044 // The revision of the catalog tree is given by the root catalog revision
1045
2/2
✓ Branch 1 taken 2668 times.
✓ Branch 2 taken 1603 times.
4271 if (catalogs_.empty()) {
1046
1/2
✓ Branch 1 taken 2570 times.
✗ Branch 2 not taken.
2668 revision_cache_ = new_catalog->GetRevision();
1047
1/2
✓ Branch 1 taken 2570 times.
✗ Branch 2 not taken.
2668 timestamp_cache_ = new_catalog->GetLastModified();
1048 2668 statistics_.catalog_revision->Set(revision_cache_);
1049
1/2
✓ Branch 1 taken 2570 times.
✗ Branch 2 not taken.
2668 has_authz_cache_ = new_catalog->GetVOMSAuthz(&authz_cache_);
1050 2668 volatile_flag_ = new_catalog->volatile_flag();
1051 }
1052
1053
1/2
✓ Branch 1 taken 4271 times.
✗ Branch 2 not taken.
4271 catalogs_.push_back(new_catalog);
1054
1/2
✓ Branch 1 taken 4271 times.
✗ Branch 2 not taken.
4271 ActivateCatalog(new_catalog);
1055 4271 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 4266 void AbstractCatalogManager<CatalogT>::DetachCatalog(CatalogT *catalog) {
1069
2/2
✓ Branch 1 taken 1521 times.
✓ Branch 2 taken 2745 times.
4266 if (catalog->HasParent())
1070
1/2
✓ Branch 2 taken 1521 times.
✗ Branch 3 not taken.
1521 catalog->parent()->RemoveChild(catalog);
1071
1072 4266 ReleaseInodes(catalog->inode_range());
1073
1/2
✓ Branch 1 taken 4266 times.
✗ Branch 2 not taken.
4266 UnloadCatalog(catalog);
1074
1075 // Delete catalog from internal lists
1076 4266 typename CatalogList::iterator i;
1077 4266 typename CatalogList::const_iterator iend;
1078
1/2
✓ Branch 5 taken 6700 times.
✗ Branch 6 not taken.
6700 for (i = catalogs_.begin(), iend = catalogs_.end(); i != iend; ++i) {
1079
2/2
✓ Branch 1 taken 4266 times.
✓ Branch 2 taken 2434 times.
6700 if (*i == catalog) {
1080
1/2
✓ Branch 2 taken 4266 times.
✗ Branch 3 not taken.
4266 catalogs_.erase(i);
1081
1/2
✓ Branch 0 taken 4266 times.
✗ Branch 1 not taken.
4266 delete catalog;
1082 4266 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 4187 void AbstractCatalogManager<CatalogT>::DetachSubtree(CatalogT *catalog) {
1098 // Detach all child catalogs recursively
1099 4187 typename CatalogList::const_iterator i;
1100 4187 typename CatalogList::const_iterator iend;
1101
1/2
✓ Branch 1 taken 4187 times.
✗ Branch 2 not taken.
4187 CatalogList catalogs_to_detach = catalog->GetChildren();
1102 4187 for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end();
1103
2/2
✓ Branch 2 taken 1454 times.
✓ Branch 3 taken 4187 times.
5641 i != iend; ++i) {
1104
1/2
✓ Branch 2 taken 1454 times.
✗ Branch 3 not taken.
1454 DetachSubtree(*i);
1105 }
1106
1107
1/2
✓ Branch 1 taken 4187 times.
✗ Branch 2 not taken.
4187 DetachCatalog(catalog);
1108 4187 }
1109
1110
1111 /**
1112 * Detaches all nested catalogs that are not on a prefix of the given tree.
1113 * Used when the catalog_watermark_ is surpassed.
1114 */
1115 template<class CatalogT>
1116 36 void AbstractCatalogManager<CatalogT>::DetachSiblings(
1117 const PathString &current_tree) {
1118 bool again;
1119
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 27 times.
36 do {
1120 36 again = false;
1121 36 unsigned N = catalogs_.size();
1122
2/2
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 27 times.
108 for (unsigned i = 0; i < N; ++i) {
1123
5/9
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 81 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 81 times.
✗ Branch 9 not taken.
✓ Branch 13 taken 9 times.
✓ Branch 14 taken 72 times.
81 if (!HasPrefix(current_tree.ToString(),
1124 81 catalogs_[i]->mountpoint().ToString(),
1125 false /* ignore_case */)) {
1126 9 DetachSubtree(catalogs_[i]);
1127 9 again = true;
1128 9 break;
1129 }
1130 }
1131 } while (again);
1132 27 perf::Inc(statistics_.n_detach_siblings);
1133 27 }
1134
1135
1136 /**
1137 * Formats the catalog hierarchy
1138 */
1139 template<class CatalogT>
1140 string AbstractCatalogManager<CatalogT>::PrintHierarchyRecursively(
1141 const CatalogT *catalog, const int level) const {
1142 string output;
1143
1144 // Indent according to level
1145 for (int i = 0; i < level; ++i)
1146 output += " ";
1147
1148 output += "-> "
1149 + string(catalog->mountpoint().GetChars(),
1150 catalog->mountpoint().GetLength())
1151 + "\n";
1152
1153 CatalogList children = catalog->GetChildren();
1154 typename CatalogList::const_iterator i = children.begin();
1155 typename CatalogList::const_iterator 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 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 2329 void AbstractCatalogManager<CatalogT>::EnforceSqliteMemLimit() {
1194 char *mem_enforced = static_cast<char *>(
1195 2329 pthread_getspecific(pkey_sqlitemem_));
1196
2/2
✓ Branch 0 taken 548 times.
✓ Branch 1 taken 1781 times.
2329 if (mem_enforced == NULL) {
1197 548 sqlite3_soft_heap_limit(kSqliteMemPerThread);
1198 548 pthread_setspecific(pkey_sqlitemem_, this);
1199 }
1200 2329 }
1201
1202 } // namespace catalog
1203
1204
1205 #endif // CVMFS_CATALOG_MGR_IMPL_H_
1206 // NOLINTEND
1207