GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_mgr.h
Date: 2026-08-23 02:40:52
Exec Total Coverage
Lines: 108 142 76.1%
Branches: 44 94 46.8%

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