GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_mgr.h
Date: 2026-05-19 11:45:12
Exec Total Coverage
Lines: 107 141 75.9%
Branches: 47 100 47.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #ifndef CVMFS_CATALOG_MGR_H_
6 #define CVMFS_CATALOG_MGR_H_
7
8 #include <inttypes.h>
9 #include <pthread.h>
10
11 #include <cassert>
12 #include <map>
13 #include <string>
14 #include <vector>
15
16 #include "catalog.h"
17 #include "crypto/hash.h"
18 #include "directory_entry.h"
19 #include "file_chunk.h"
20 #include "manifest_fetch.h"
21 #include "statistics.h"
22 #include "util/algorithm.h"
23 #include "util/atomic.h"
24 #include "util/logging.h"
25 #include "util/platform.h"
26
27 class XattrList;
28 namespace catalog {
29
30 const unsigned kSqliteMemPerThread = 1 * 1024 * 1024;
31
32
33 /**
34 * LookupOption for a directory entry (bitmask).
35 * kLookupDefault = Look solely at the given directory entry (parent is ignored)
36 * kLookupRawSymlink = Don't resolve environment variables in symlink targets
37 */
38 typedef unsigned LookupOptions;
39 const unsigned kLookupDefault = 0b1;
40 const unsigned kLookupRawSymlink = 0b10;
41
42 /**
43 * Results upon loading a catalog file.
44 */
45 enum LoadReturn {
46 kLoadNew = 0,
47 kLoadUp2Date,
48 kLoadNoSpace,
49 kLoadFail,
50
51 kLoadNumEntries
52 };
53
54 /**
55 * Location of the most recent root catalog.
56 * Used as part of the process of loading a catalog.
57 * - GetNewRootCatalogContext() sets the location within the CatalogContext obj
58 * - LoadCatalogByHash(): when loading a root catalog it uses the location
59 * stored within the CatalogContext object to retrieve
60 * the root catalog from the right location
61 */
62 enum RootCatalogLocation {
63 kCtlgNoLocationNeeded = 0, // hash known, no location needed
64 kCtlgLocationMounted, // already loaded in mounted_catalogs_
65 kCtlgLocationServer,
66 kCtlgLocationBreadcrumb
67 };
68
69 /**
70 * CatalogContext class contains all necessary information to load a catalog and
71 * also keeps track of the resulting output.
72 * It works as follows:
73 * 1) Load a new root catalog:
74 * - Use empty constructor CatalogContext()
75 * - Let the CatalogContext object be populated by GetNewRootCatalogContext()
76 * - This will set: hash, mountpoint, root_ctlg_revision, root_ctlg_location
77 * - Call LoadCatalogByHash()
78 * - This will set: sqlite_path
79 * 2) Load a catalog based on a given hash
80 * - Populate CatalogContext object; used constructor depends on catalog type
81 * - Root catalog: CatalogContext(shash::Any hash, PathString mountpoint,
82 RootCatalogLocation location)
83 - Nested catalog: CatalogContext(shash::Any hash, PathString mountpoint)
84 - Note: in this case root_ctlg_revision is not used
85 * - Call LoadCatalogByHash()
86 - This will set: sqlite_path
87 */
88 struct CatalogContext {
89 public:
90 86 CatalogContext()
91 86 : hash_(shash::Any())
92 86 , mountpoint_(PathString("invalid", 7))
93 , // empty str is root ctlg
94
1/2
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
86 sqlite_path_("")
95 86 , root_ctlg_revision_(-1ul)
96 86 , root_ctlg_location_(kCtlgNoLocationNeeded)
97
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 , manifest_ensemble_(NULL) { }
98 120 CatalogContext(const shash::Any &hash, const PathString &mountpoint)
99 120 : hash_(hash)
100 120 , mountpoint_(mountpoint)
101
1/2
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
120 , sqlite_path_("")
102 120 , root_ctlg_revision_(-1ul)
103 120 , root_ctlg_location_(kCtlgNoLocationNeeded)
104
1/2
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
120 , manifest_ensemble_(NULL) { }
105
106 2504 CatalogContext(const shash::Any &hash, const PathString &mountpoint,
107 const RootCatalogLocation location)
108 2504 : hash_(hash)
109 2504 , mountpoint_(mountpoint)
110
1/2
✓ Branch 2 taken 2504 times.
✗ Branch 3 not taken.
2504 , sqlite_path_("")
111 2504 , root_ctlg_revision_(-1ul)
112 2504 , root_ctlg_location_(location)
113
1/2
✓ Branch 1 taken 2504 times.
✗ Branch 2 not taken.
2504 , manifest_ensemble_(NULL) { }
114
115 3756 bool IsRootCatalog() { return mountpoint_.IsEmpty(); }
116
117 813 std::string *GetSqlitePathPtr() { return &sqlite_path_; }
118 shash::Any *GetHashPtr() { return &hash_; }
119
120 7987 shash::Any hash() const { return hash_; }
121 3760 PathString mountpoint() const { return mountpoint_; }
122 2365 std::string sqlite_path() const { return sqlite_path_; }
123 uint64_t root_ctlg_revision() const { return root_ctlg_revision_; }
124 663 RootCatalogLocation root_ctlg_location() const { return root_ctlg_location_; }
125 766 manifest::ManifestEnsemble *manifest_ensemble() const {
126 766 return manifest_ensemble_.weak_ref();
127 }
128
129 1447 void SetHash(shash::Any hash) { hash_ = hash; }
130 1522 void SetMountpoint(const PathString &mountpoint) { mountpoint_ = mountpoint; }
131 1859 void SetSqlitePath(const std::string &sqlite_path) {
132 1859 sqlite_path_ = sqlite_path;
133 1859 }
134 243 void SetRootCtlgRevision(uint64_t root_ctlg_revision) {
135 243 root_ctlg_revision_ = root_ctlg_revision;
136 243 }
137 1766 void SetRootCtlgLocation(RootCatalogLocation root_ctlg_location) {
138 1766 root_ctlg_location_ = root_ctlg_location;
139 1766 }
140 /**
141 * Gives ownership to CatalogContext
142 */
143 160 void TakeManifestEnsemble(manifest::ManifestEnsemble *manifest_ensemble) {
144 160 manifest_ensemble_ = manifest_ensemble;
145 160 }
146
147
148 private:
149 // mandatory for LoadCatalogByHash()
150 shash::Any hash_;
151 // mandatory for LoadCatalogByHash()
152 PathString mountpoint_;
153 // out parameter, path name of the sqlite catalog
154 std::string sqlite_path_;
155 // root catalog: revision is needed for GetNewRootCatalogContext()
156 uint64_t root_ctlg_revision_;
157 // root catalog: location is mandatory for LoadCatalogByHash()
158 RootCatalogLocation root_ctlg_location_;
159 // root catalog: if location = server mandatory for LoadCatalogByHash()
160 UniquePtr<manifest::ManifestEnsemble> manifest_ensemble_;
161 };
162
163 inline const char *Code2Ascii(const LoadReturn error) {
164 const char *texts[kLoadNumEntries + 1];
165 texts[0] = "loaded new catalog";
166 texts[1] = "catalog was up to date";
167 texts[2] = "not enough space to load catalog";
168 texts[3] = "failed to load catalog";
169 texts[4] = "no text";
170 return texts[error];
171 }
172
173
174 struct Statistics {
175 perf::Counter *n_lookup_inode;
176 perf::Counter *n_lookup_path;
177 perf::Counter *n_lookup_path_negative;
178 perf::Counter *n_lookup_xattrs;
179 perf::Counter *n_listing;
180 perf::Counter *n_nested_listing;
181 perf::Counter *n_detach_siblings;
182 perf::Counter *n_write_lock;
183 perf::Counter *ns_write_lock;
184
185 perf::Counter *catalog_revision;
186
187 1700 explicit Statistics(perf::Statistics *statistics) {
188
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_lookup_inode = statistics->Register("catalog_mgr.n_lookup_inode",
189 "Number of inode lookups");
190
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_lookup_path = statistics->Register("catalog_mgr.n_lookup_path",
191 "Number of path lookups");
192
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_lookup_path_negative = statistics->Register(
193 "catalog_mgr.n_lookup_path_negative",
194 "Number of negative path lookups");
195
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_lookup_xattrs = statistics->Register("catalog_mgr.n_lookup_xattrs",
196 "Number of xattrs lookups");
197
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_listing = statistics->Register("catalog_mgr.n_listing",
198 "Number of listings");
199
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_nested_listing = statistics->Register(
200 "catalog_mgr.n_nested_listing",
201 "Number of listings of nested catalogs");
202
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_detach_siblings = statistics->Register(
203 "catalog_mgr.n_detach_siblings",
204 "Number of times the CVMFS_CATALOG_WATERMARK was hit");
205
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 n_write_lock = statistics->Register("catalog_mgr.n_write_lock",
206 "number of write lock calls");
207
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 ns_write_lock = statistics->Register("catalog_mgr.ns_write_lock",
208 "time spent in WriteLock() [ns]");
209
3/6
✓ Branch 2 taken 1700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1700 times.
✗ Branch 10 not taken.
1700 catalog_revision = statistics->Register(
210 "catalog_revision", "Revision number of the root file catalog");
211 1700 }
212 };
213
214
215 template<class CatalogT>
216 class AbstractCatalogManager;
217
218
219 /**
220 * This class provides the read-only interface to a tree of catalogs
221 * representing a (subtree of a) repository.
222 * Mostly lookup functions filling DirectoryEntry objects.
223 * Reloading of expired catalogs, attaching of nested catalogs and delegating
224 * of lookups to the appropriate catalog is done transparently.
225 *
226 * The loading / creating of catalogs is up to derived classes.
227 *
228 * CatalogT is either Catalog or MockCatalog.
229 *
230 * Usage:
231 * DerivedCatalogManager *catalog_manager = new DerivedCatalogManager();
232 * catalog_manager->Init();
233 * catalog_manager->Lookup(<inode>, &<result_entry>);
234 */
235 template<class CatalogT>
236 class AbstractCatalogManager : public SingleCopy {
237 public:
238 typedef std::vector<CatalogT *> CatalogList;
239 typedef CatalogT catalog_t;
240
241 static const inode_t kInodeOffset = 255;
242 explicit AbstractCatalogManager(perf::Statistics *statistics);
243 virtual ~AbstractCatalogManager();
244
245 void SetInodeAnnotation(InodeAnnotation *new_annotation);
246 virtual bool Init();
247 LoadReturn RemountDryrun();
248 LoadReturn Remount();
249 LoadReturn ChangeRoot(const shash::Any &root_hash);
250 void DetachNested();
251
252 bool LookupPath(const PathString &path, const LookupOptions options,
253 DirectoryEntry *entry);
254 571 bool LookupPath(const std::string &path, const LookupOptions options,
255 DirectoryEntry *entry) {
256 571 PathString p;
257
1/2
✓ Branch 3 taken 571 times.
✗ Branch 4 not taken.
571 p.Assign(&path[0], path.length());
258
1/2
✓ Branch 1 taken 571 times.
✗ Branch 2 not taken.
1142 return LookupPath(p, options, entry);
259 571 }
260 bool LookupXattrs(const PathString &path, XattrList *xattrs);
261
262 bool LookupNested(const PathString &path,
263 PathString *mountpoint,
264 shash::Any *hash,
265 uint64_t *size);
266 bool ListCatalogSkein(const PathString &path,
267 std::vector<PathString> *result_list);
268
269 bool Listing(const PathString &path, DirectoryEntryList *listing,
270 const bool expand_symlink);
271 1172 bool Listing(const PathString &path, DirectoryEntryList *listing) {
272 1172 return Listing(path, listing, true);
273 }
274 436 bool Listing(const std::string &path, DirectoryEntryList *listing) {
275 436 PathString p;
276
1/2
✓ Branch 3 taken 436 times.
✗ Branch 4 not taken.
436 p.Assign(&path[0], path.length());
277
1/2
✓ Branch 1 taken 436 times.
✗ Branch 2 not taken.
872 return Listing(p, listing);
278 436 }
279 bool ListingStat(const PathString &path, StatEntryList *listing);
280
281 bool ListFileChunks(const PathString &path,
282 const shash::Algorithms interpret_hashes_as,
283 FileChunkList *chunks);
284 void SetOwnerMaps(const OwnerMap &uid_map, const OwnerMap &gid_map);
285 void SetCatalogWatermark(unsigned limit);
286
287 shash::Any GetNestedCatalogHash(const PathString &mountpoint);
288
289 12 Statistics statistics() const { return statistics_; }
290 uint64_t inode_gauge() {
291 ReadLock();
292 uint64_t r = inode_gauge_;
293 Unlock();
294 return r;
295 }
296 378 bool volatile_flag() const { return volatile_flag_; }
297 uint64_t GetRevision() const;
298 uint64_t GetTimestamp() const;
299 uint64_t GetTTL() const;
300 bool HasExplicitTTL() const;
301 bool GetVOMSAuthz(std::string *authz) const;
302 int GetNumCatalogs() const;
303 std::string PrintHierarchy() const;
304 std::string PrintAllMemStatistics() const;
305
306 /**
307 * Get the inode number of the root DirectoryEntry
308 * ('root' means the root of the whole file system)
309 * @return the root inode number
310 */
311 28 inline inode_t GetRootInode() const {
312
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 27 times.
28 return inode_annotation_ ? inode_annotation_->Annotate(kInodeOffset + 1)
313 28 : kInodeOffset + 1;
314 }
315 18019 inline CatalogT *GetRootCatalog() const { return catalogs_.front(); }
316 /**
317 * Inodes are ambiquitous under some circumstances, to prevent problems
318 * they must be passed through this method first
319 * @param inode the raw inode
320 * @return the revised inode
321 */
322 inline inode_t MangleInode(const inode_t inode) const {
323 return (inode <= kInodeOffset) ? GetRootInode() : inode;
324 }
325
326 catalog::Counters LookupCounters(const PathString &path,
327 std::string *subcatalog_path,
328 shash::Any *hash);
329
330 protected:
331 /**
332 * Load the catalog and return a file name and the catalog hash.
333 *
334 * GetNewRootCatalogContext() populates CatalogContext object with the
335 * information needed to retrieve the most recent root catalog independent of
336 * its location.
337 * The CatalogContext object must be populated with at least hash and
338 * mountpoint to call LoadCatalogByHash().
339 *
340 * See class description of CatalogContext for more information.
341 */
342 virtual LoadReturn GetNewRootCatalogContext(CatalogContext *result) = 0;
343 virtual LoadReturn LoadCatalogByHash(CatalogContext *ctlg_context) = 0;
344 3117 virtual void UnloadCatalog(const CatalogT *catalog) { }
345 272 virtual void ActivateCatalog(CatalogT *catalog) { }
346 1 const std::vector<CatalogT *> &GetCatalogs() const { return catalogs_; }
347
348 /**
349 * Opportunistic optimization: the client catalog manager uses this method
350 * to preload into the cache a nested catalog that is likely to be required
351 * next. Likely, because there is a race with the root catalog reload which
352 * may result in the wrong catalog being staged. That's not a fault though,
353 * the correct catalog will still be loaded with the write lock held.
354 * Note that this method is never used for root catalogs.
355 */
356 201 virtual void StageNestedCatalogByHash(const shash::Any & /*hash*/,
357 201 const PathString & /*mountpoint*/) { }
358 /**
359 * Called within the ReadLock(), which will be released before downloading
360 * the catalog (and before leaving the method)
361 */
362 void StageNestedCatalogAndUnlock(const PathString &path,
363 const CatalogT *parent,
364 bool is_listable);
365
366 /**
367 * Create a new Catalog object.
368 * Every derived class has to implement this and return a newly
369 * created (derived) Catalog structure of it's desired type.
370 * @param mountpoint the future mountpoint of the catalog to create
371 * @param catalog_hash the content hash of the catalog database
372 * @param parent_catalog the parent of the catalog to create
373 * @return a newly created (derived) Catalog
374 */
375 virtual CatalogT *CreateCatalog(const PathString &mountpoint,
376 const shash::Any &catalog_hash,
377 CatalogT *parent_catalog) = 0;
378
379 CatalogT *MountCatalog(const PathString &mountpoint, const shash::Any &hash,
380 CatalogT *parent_catalog);
381 bool MountSubtree(const PathString &path,
382 const CatalogT *entry_point,
383 bool can_listing,
384 CatalogT **leaf_catalog);
385
386 CatalogT *LoadFreeCatalog(const PathString &mountpoint,
387 const shash::Any &hash);
388
389 bool AttachCatalog(const std::string &db_path, CatalogT *new_catalog);
390 void DetachCatalog(CatalogT *catalog);
391 void DetachSubtree(CatalogT *catalog);
392 void DetachSiblings(const PathString &current_tree);
393 1700 void DetachAll() {
394
2/2
✓ Branch 1 taken 1604 times.
✓ Branch 2 taken 96 times.
1700 if (!catalogs_.empty())
395 1604 DetachSubtree(GetRootCatalog());
396 1700 }
397 bool IsAttached(const PathString &root_path,
398 CatalogT **attached_catalog) const;
399
400 CatalogT *FindCatalog(const PathString &path) const;
401
402 uint64_t GetRevisionNoLock() const;
403 uint64_t GetTimestampNoLock() const;
404 4131 inline void ReadLock() const {
405 4131 const int retval = pthread_rwlock_rdlock(rwlock_);
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4131 times.
4131 assert(retval == 0);
407 4131 }
408 2149 inline void WriteLock() const {
409 2149 const uint64_t timestamp = platform_monotonic_time_ns();
410 2149 const int retval = pthread_rwlock_wrlock(rwlock_);
411
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2149 times.
2149 assert(retval == 0);
412 2149 perf::Inc(statistics_.n_write_lock);
413 2149 const uint64_t duration = platform_monotonic_time_ns() - timestamp;
414 2149 perf::Xadd(statistics_.ns_write_lock, duration);
415 2149 }
416 6280 inline void Unlock() const {
417 6280 const int retval = pthread_rwlock_unlock(rwlock_);
418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6280 times.
6280 assert(retval == 0);
419 6280 }
420 virtual void EnforceSqliteMemLimit();
421
422 private:
423 void CheckInodeWatermark();
424
425 /**
426 * The flat list of all attached catalogs.
427 */
428 CatalogList catalogs_;
429 int inode_watermark_status_; /**< 0: OK, 1: > 32bit */
430 uint64_t inode_gauge_; /**< highest issued inode */
431 uint64_t revision_cache_;
432 uint64_t timestamp_cache_;
433 /**
434 * Try to keep number of nested catalogs below the given limit. Zero means no
435 * limit. Surpassing the watermark on mounting a catalog triggers
436 * a DetachSiblings() call.
437 */
438 unsigned catalog_watermark_;
439 /**
440 * Not protected by a read lock because it can only change when the root
441 * catalog is exchanged (during big global lock of the file system).
442 */
443 bool volatile_flag_;
444 /**
445 * Saves the result of GetVOMSAuthz when a root catalog is attached
446 */
447 bool has_authz_cache_;
448 /**
449 * Saves the VOMS requirements when a root catalog is attached
450 */
451 std::string authz_cache_;
452 /**
453 * Counts how often the inodes have been invalidated.
454 */
455 uint64_t incarnation_;
456 // TODO(molina) we could just add an atomic global counter instead
457 InodeAnnotation *inode_annotation_; /**< applied to all catalogs */
458 pthread_rwlock_t *rwlock_;
459 Statistics statistics_;
460 pthread_key_t pkey_sqlitemem_;
461 OwnerMap uid_map_;
462 OwnerMap gid_map_;
463
464 // Not needed anymore since there are the glue buffers
465 // Catalog *Inode2Catalog(const inode_t inode);
466 std::string PrintHierarchyRecursively(const CatalogT *catalog,
467 const int level) const;
468 std::string PrintMemStatsRecursively(const CatalogT *catalog) const;
469
470 InodeRange AcquireInodes(uint64_t size);
471 void ReleaseInodes(const InodeRange chunk);
472 }; // class CatalogManager
473
474 class InodeGenerationAnnotation : public InodeAnnotation {
475 public:
476 480 InodeGenerationAnnotation() { inode_offset_ = 0; }
477 1918 virtual ~InodeGenerationAnnotation() { }
478 virtual bool ValidInode(const uint64_t inode) {
479 return inode >= inode_offset_;
480 }
481 1 virtual inode_t Annotate(const inode_t raw_inode) {
482 1 return raw_inode + inode_offset_;
483 }
484 virtual inode_t Strip(const inode_t annotated_inode) {
485 return annotated_inode - inode_offset_;
486 }
487 1 virtual void IncGeneration(const uint64_t by) {
488 1 inode_offset_ += by;
489 1 LogCvmfs(kLogCatalog, kLogDebug, "set inode generation to %lu",
490 inode_offset_);
491 1 }
492 219 virtual inode_t GetGeneration() { return inode_offset_; }
493
494 private:
495 uint64_t inode_offset_;
496 };
497
498 /**
499 * In NFS mode, the root inode has to be always 256. Otherwise the inode maps
500 * lookup fails. In general, the catalog manager inodes in NFS mode are only
501 * used for the chunk tables.
502 */
503 class InodeNfsGenerationAnnotation : public InodeAnnotation {
504 public:
505 InodeNfsGenerationAnnotation() { inode_offset_ = 0; }
506 virtual ~InodeNfsGenerationAnnotation() { }
507 virtual bool ValidInode(const uint64_t inode) {
508 return (inode >= inode_offset_) || (inode == kRootInode);
509 }
510 virtual inode_t Annotate(const inode_t raw_inode) {
511 if (raw_inode <= kRootInode)
512 return kRootInode;
513 return raw_inode + inode_offset_;
514 }
515 virtual inode_t Strip(const inode_t annotated_inode) {
516 if (annotated_inode == kRootInode)
517 return annotated_inode;
518 return annotated_inode - inode_offset_;
519 }
520 virtual void IncGeneration(const uint64_t by) {
521 inode_offset_ += by;
522 LogCvmfs(kLogCatalog, kLogDebug, "set inode generation to %lu",
523 inode_offset_);
524 }
525 virtual inode_t GetGeneration() { return inode_offset_; }
526
527 private:
528 static const uint64_t
529 kRootInode = AbstractCatalogManager<Catalog>::kInodeOffset + 1;
530 uint64_t inode_offset_;
531 };
532
533 } // namespace catalog
534
535 #include "catalog_mgr_impl.h"
536
537 #endif // CVMFS_CATALOG_MGR_H_
538