| 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 ¤t_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 |