Directory: | cvmfs/ |
---|---|
File: | cvmfs/catalog.h |
Date: | 2025-06-22 02:36:02 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 46 | 53 | 86.8% |
Branches: | 17 | 40 | 42.5% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #ifndef CVMFS_CATALOG_H_ | ||
6 | #define CVMFS_CATALOG_H_ | ||
7 | |||
8 | #include <pthread.h> | ||
9 | #include <stdint.h> | ||
10 | |||
11 | #include <cassert> | ||
12 | #include <map> | ||
13 | #include <string> | ||
14 | #include <vector> | ||
15 | |||
16 | #include "catalog_counters.h" | ||
17 | #include "catalog_sql.h" | ||
18 | #include "crypto/hash.h" | ||
19 | #include "directory_entry.h" | ||
20 | #include "file_chunk.h" | ||
21 | #include "gtest/gtest_prod.h" | ||
22 | #include "shortstring.h" | ||
23 | #include "sql.h" | ||
24 | #include "uid_map.h" | ||
25 | #include "xattr.h" | ||
26 | |||
27 | namespace swissknife { | ||
28 | class CommandMigrate; | ||
29 | } | ||
30 | |||
31 | namespace catalog { | ||
32 | |||
33 | template<class CatalogT> | ||
34 | class AbstractCatalogManager; | ||
35 | |||
36 | class Catalog; | ||
37 | |||
38 | class Counters; | ||
39 | |||
40 | typedef std::vector<Catalog *> CatalogList; | ||
41 | typedef IntegerMap<uint64_t> OwnerMap; // used to map uid/gid | ||
42 | |||
43 | |||
44 | /** | ||
45 | * Every Catalog gets an InodeRange assigned when attached to | ||
46 | * a CatalogManager. Inodes are assigned at runtime out of this InodeRange. | ||
47 | * An inode is computed by <row ID of entry> + offset | ||
48 | */ | ||
49 | struct InodeRange { | ||
50 | uint64_t offset; | ||
51 | uint64_t size; | ||
52 | |||
53 | 9639 | InodeRange() : offset(0), size(0) { } | |
54 | |||
55 | inline bool ContainsInode(const inode_t inode) const { | ||
56 | return ((inode > offset) && (inode <= size + offset)); | ||
57 | } | ||
58 | |||
59 | 519 | inline void MakeDummy() { offset = 1; } | |
60 | |||
61 | 77271 | inline bool IsInitialized() const { return offset > 0; } | |
62 |
3/4✓ Branch 1 taken 25073 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 178 times.
✓ Branch 4 taken 24895 times.
|
25073 | inline bool IsDummy() const { return IsInitialized() && size == 0; } |
63 | }; | ||
64 | |||
65 | |||
66 | /** | ||
67 | * Allows to define a class that transforms the inode in order to ensure | ||
68 | * that inodes are not reused after reloads (catalog or fuse module). | ||
69 | * Currently, annotation is used to set an offset starting at the highest | ||
70 | * so far issued inode. The implementation is in the catalog manager. | ||
71 | */ | ||
72 | class InodeAnnotation { | ||
73 | public: | ||
74 | 3038 | virtual ~InodeAnnotation() { } | |
75 | virtual inode_t Annotate(const inode_t raw_inode) = 0; | ||
76 | virtual void IncGeneration(const uint64_t by) = 0; | ||
77 | virtual inode_t GetGeneration() = 0; | ||
78 | virtual bool ValidInode(const uint64_t inode) = 0; | ||
79 | virtual inode_t Strip(const inode_t annotated_inode) = 0; | ||
80 | }; | ||
81 | |||
82 | |||
83 | /** | ||
84 | * This class wraps a catalog database and provides methods | ||
85 | * to query for directory entries. | ||
86 | * It has a pointer to its parent catalog and its children, thereby creating | ||
87 | * a tree structure of nested catalogs. | ||
88 | * | ||
89 | * Read-only catalog. A sub-class provides read-write access. | ||
90 | */ | ||
91 | class Catalog : SingleCopy { | ||
92 | FRIEND_TEST(T_Catalog, NormalizePath); | ||
93 | FRIEND_TEST(T_Catalog, PlantPath); | ||
94 | friend class swissknife::CommandMigrate; // for catalog version migration | ||
95 | |||
96 | public: | ||
97 | typedef std::vector<shash::Any> HashVector; | ||
98 | |||
99 | /** | ||
100 | * The default TTL should be shorter than the autofs idle unmount time | ||
101 | * which is 5 minutes, because the config repo is accessed on every root | ||
102 | * catalog refresh and we want to avoid thrashing that mountpoint. | ||
103 | */ | ||
104 | static const uint64_t kDefaultTTL = 240; /**< 4 minutes default TTL */ | ||
105 | |||
106 | /** | ||
107 | * Note: is_nested only has an effect if parent == NULL otherwise being | ||
108 | * a root catalog is determined by having a parent pointer or not. | ||
109 | */ | ||
110 | Catalog(const PathString &mountpoint, | ||
111 | const shash::Any &catalog_hash, | ||
112 | Catalog *parent, | ||
113 | const bool is_nested = false); | ||
114 | virtual ~Catalog(); | ||
115 | |||
116 | static Catalog *AttachFreely(const std::string &imaginary_mountpoint, | ||
117 | const std::string &file, | ||
118 | const shash::Any &catalog_hash, | ||
119 | Catalog *parent = NULL, | ||
120 | const bool is_nested = false); | ||
121 | |||
122 | bool OpenDatabase(const std::string &db_path); | ||
123 | |||
124 | 19358 | inline bool LookupPath(const PathString &path, DirectoryEntry *dirent) const { | |
125 |
1/2✓ Branch 2 taken 19358 times.
✗ Branch 3 not taken.
|
19358 | return LookupMd5Path(NormalizePath(path), dirent); |
126 | } | ||
127 | bool LookupRawSymlink(const PathString &path, LinkString *raw_symlink) const; | ||
128 | 5 | bool LookupXattrsPath(const PathString &path, XattrList *xattrs) const { | |
129 |
1/2✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | return LookupXattrsMd5Path(NormalizePath(path), xattrs); |
130 | } | ||
131 | |||
132 | 3037 | inline bool ListingPath(const PathString &path, | |
133 | DirectoryEntryList *listing, | ||
134 | const bool expand_symlink = true) const { | ||
135 |
1/2✓ Branch 2 taken 3037 times.
✗ Branch 3 not taken.
|
3037 | return ListingMd5Path(NormalizePath(path), listing, expand_symlink); |
136 | } | ||
137 | 117 | bool ListingPathStat(const PathString &path, StatEntryList *listing) const { | |
138 |
1/2✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
|
117 | return ListingMd5PathStat(NormalizePath(path), listing); |
139 | } | ||
140 | bool AllChunksBegin(); | ||
141 | bool AllChunksNext(shash::Any *hash, zlib::Algorithms *compression_alg); | ||
142 | bool AllChunksEnd(); | ||
143 | |||
144 | 7 | inline bool ListPathChunks(const PathString &path, | |
145 | const shash::Algorithms interpret_hashes_as, | ||
146 | FileChunkList *chunks) const { | ||
147 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | return ListMd5PathChunks(NormalizePath(path), interpret_hashes_as, chunks); |
148 | } | ||
149 | |||
150 | CatalogList GetChildren() const; | ||
151 | Catalog *FindSubtree(const PathString &path) const; | ||
152 | Catalog *FindChild(const PathString &mountpoint) const; | ||
153 | void AddChild(Catalog *child); | ||
154 | void RemoveChild(Catalog *child); | ||
155 | |||
156 | const HashVector &GetReferencedObjects() const; | ||
157 | void TakeDatabaseFileOwnership(); | ||
158 | void DropDatabaseFileOwnership(); | ||
159 | 29 | bool OwnsDatabaseFile() const { | |
160 |
5/6✓ Branch 0 taken 22 times.
✓ Branch 1 taken 7 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 17 times.
|
29 | return ((database_ != NULL) && database_->OwnsFile()) || managed_database_; |
161 | } | ||
162 | |||
163 | uint64_t GetTTL() const; | ||
164 | bool HasExplicitTTL() const; | ||
165 | uint64_t GetRevision() const; | ||
166 | bool GetVOMSAuthz(std::string *authz) const; | ||
167 | uint64_t GetLastModified() const; | ||
168 | uint64_t GetNumEntries() const; | ||
169 | uint64_t GetNumChunks() const; | ||
170 | shash::Any GetPreviousRevision() const; | ||
171 | 1579 | const Counters &GetCounters() const { return counters_; } | |
172 | std::string PrintMemStatistics() const; | ||
173 | |||
174 | 25087 | inline float schema() const { return database().schema_version(); } | |
175 | 73258 | inline PathString mountpoint() const { return mountpoint_; } | |
176 | 4897 | inline Catalog *parent() const { return parent_; } | |
177 | 4027 | inline uint64_t max_row_id() const { return max_row_id_; } | |
178 | 4019 | inline InodeRange inode_range() const { return inode_range_; } | |
179 | 4541 | inline void set_inode_range(const InodeRange value) { inode_range_ = value; } | |
180 | 3774 | inline std::string database_path() const { return database_->filename(); } | |
181 | 811 | inline PathString root_prefix() const { return root_prefix_; } | |
182 | 201 | inline shash::Any hash() const { return catalog_hash_; } | |
183 | 2575 | inline bool volatile_flag() const { return volatile_flag_; } | |
184 | 131 | inline uint64_t revision() const { return GetRevision(); } | |
185 | |||
186 | 52198 | inline bool IsInitialized() const { | |
187 |
2/4✓ Branch 1 taken 52198 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 52198 times.
✗ Branch 4 not taken.
|
52198 | return inode_range_.IsInitialized() && initialized_; |
188 | } | ||
189 | 6433 | inline bool IsRoot() const { return is_root_; } | |
190 | ✗ | bool IsAutogenerated() const { | |
191 | ✗ | DirectoryEntry dirent; | |
192 | ✗ | assert(IsInitialized()); | |
193 | ✗ | return LookupPath(PathString(mountpoint_.ToString() + "/.cvmfsautocatalog"), | |
194 | ✗ | &dirent); | |
195 | } | ||
196 | 35948 | inline bool HasParent() const { return parent_ != NULL; } | |
197 | 10 | inline virtual bool IsWritable() const { return false; } | |
198 | |||
199 | typedef struct { | ||
200 | PathString mountpoint; | ||
201 | shash::Any hash; | ||
202 | uint64_t size; | ||
203 | } NestedCatalog; | ||
204 | typedef std::vector<NestedCatalog> NestedCatalogList; | ||
205 | const NestedCatalogList &ListNestedCatalogs() const; | ||
206 | const NestedCatalogList ListOwnNestedCatalogs() const; | ||
207 | bool FindNested(const PathString &mountpoint, shash::Any *hash, | ||
208 | uint64_t *size) const; | ||
209 | |||
210 | void SetInodeAnnotation(InodeAnnotation *new_annotation); | ||
211 | inode_t GetMangledInode(const uint64_t row_id, | ||
212 | const uint64_t hardlink_group) const; | ||
213 | |||
214 | void SetOwnerMaps(const OwnerMap *uid_map, const OwnerMap *gid_map); | ||
215 | 25073 | uint64_t MapUid(const uint64_t uid) const { | |
216 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25073 times.
|
25073 | if (uid_map_) { |
217 | ✗ | return uid_map_->Map(uid); | |
218 | } | ||
219 | 25073 | return uid; | |
220 | } | ||
221 | 25073 | uint64_t MapGid(const uint64_t gid) const { | |
222 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25073 times.
|
25073 | if (gid_map_) { |
223 | ✗ | return gid_map_->Map(gid); | |
224 | } | ||
225 | 25073 | return gid; | |
226 | } | ||
227 | |||
228 | protected: | ||
229 | typedef std::map<uint64_t, inode_t> HardlinkGroupMap; | ||
230 | mutable HardlinkGroupMap hardlink_groups_; | ||
231 | |||
232 | pthread_mutex_t *lock_; | ||
233 | |||
234 | bool InitStandalone(const std::string &database_file); | ||
235 | bool ReadCatalogCounters(); | ||
236 | |||
237 | /** | ||
238 | * Specifies the SQLite open flags. Overwritten by r/w catalog. | ||
239 | */ | ||
240 | 1949 | virtual CatalogDatabase::OpenMode DatabaseOpenMode() const { | |
241 | 1949 | return CatalogDatabase::kOpenReadOnly; | |
242 | } | ||
243 | |||
244 | virtual void InitPreparedStatements(); | ||
245 | void FinalizePreparedStatements(); | ||
246 | |||
247 | 79 | Counters &GetWritableCounters() { return counters_; } | |
248 | |||
249 | 39100 | inline const CatalogDatabase &database() const { return *database_; } | |
250 | 121192 | inline CatalogDatabase &database() { return *database_; } | |
251 | 2975 | inline void set_parent(Catalog *catalog) { parent_ = catalog; } | |
252 | |||
253 | void ResetNestedCatalogCacheUnprotected(); | ||
254 | |||
255 | bool LookupMd5Path(const shash::Md5 &md5path, DirectoryEntry *dirent) const; | ||
256 | |||
257 | private: | ||
258 | typedef std::map<PathString, Catalog *> NestedCatalogMap; | ||
259 | |||
260 | /** | ||
261 | * The hash of the empty string. Used to identify the root entry of a | ||
262 | * repository, which is the child transition point of a bind mountpoint. | ||
263 | */ | ||
264 | static const shash::Md5 kMd5PathEmpty; | ||
265 | |||
266 | enum VomsAuthzStatus { | ||
267 | kVomsUnknown, // Not yet looked up | ||
268 | kVomsNone, // No voms_authz key in properties table | ||
269 | kVomsPresent, // voms_authz property available | ||
270 | }; | ||
271 | |||
272 | shash::Md5 NormalizePath(const PathString &path) const; | ||
273 | PathString NormalizePath2(const PathString &path) const; | ||
274 | PathString PlantPath(const PathString &path) const; | ||
275 | |||
276 | void FixTransitionPoint(const shash::Md5 &md5path, | ||
277 | DirectoryEntry *dirent) const; | ||
278 | |||
279 | bool LookupXattrsMd5Path(const shash::Md5 &md5path, XattrList *xattrs) const; | ||
280 | bool ListMd5PathChunks(const shash::Md5 &md5path, | ||
281 | const shash::Algorithms interpret_hashes_as, | ||
282 | FileChunkList *chunks) const; | ||
283 | bool ListingMd5Path(const shash::Md5 &md5path, | ||
284 | DirectoryEntryList *listing, | ||
285 | const bool expand_symlink = true) const; | ||
286 | bool ListingMd5PathStat(const shash::Md5 &md5path, | ||
287 | StatEntryList *listing) const; | ||
288 | bool LookupEntry(const shash::Md5 &md5path, const bool expand_symlink, | ||
289 | DirectoryEntry *dirent) const; | ||
290 | |||
291 | CatalogDatabase *database_; | ||
292 | |||
293 | const shash::Any catalog_hash_; | ||
294 | PathString root_prefix_; | ||
295 | /** | ||
296 | * Normally, catalogs are mounted at their root_prefix_. But for the structure | ||
297 | * under /.cvmfs/snapshots/..., that's not the case. | ||
298 | */ | ||
299 | PathString mountpoint_; | ||
300 | /** | ||
301 | * True, iff root_prefix_ == mountpoint_ | ||
302 | */ | ||
303 | bool is_regular_mountpoint_; | ||
304 | bool volatile_flag_; | ||
305 | /** | ||
306 | * For catalogs in a catalog manager: doesn't have a parent catalog | ||
307 | */ | ||
308 | const bool is_root_; | ||
309 | bool managed_database_; | ||
310 | |||
311 | Catalog *parent_; | ||
312 | NestedCatalogMap children_; | ||
313 | mutable NestedCatalogList nested_catalog_cache_; | ||
314 | mutable bool nested_catalog_cache_dirty_; | ||
315 | |||
316 | mutable VomsAuthzStatus voms_authz_status_; | ||
317 | mutable std::string voms_authz_; | ||
318 | |||
319 | bool initialized_; | ||
320 | InodeRange inode_range_; | ||
321 | uint64_t max_row_id_; | ||
322 | InodeAnnotation *inode_annotation_; | ||
323 | Counters counters_; | ||
324 | // Point to the maps in the catalog manager | ||
325 | const OwnerMap *uid_map_; | ||
326 | const OwnerMap *gid_map_; | ||
327 | |||
328 | SqlListing *sql_listing_; | ||
329 | SqlLookupPathHash *sql_lookup_md5path_; | ||
330 | SqlNestedCatalogLookup *sql_lookup_nested_; | ||
331 | SqlNestedCatalogListing *sql_list_nested_; | ||
332 | SqlOwnNestedCatalogListing *sql_own_list_nested_; | ||
333 | SqlAllChunks *sql_all_chunks_; | ||
334 | SqlChunksListing *sql_chunks_listing_; | ||
335 | SqlLookupXattrs *sql_lookup_xattrs_; | ||
336 | |||
337 | mutable HashVector referenced_hashes_; | ||
338 | }; // class Catalog | ||
339 | |||
340 | } // namespace catalog | ||
341 | |||
342 | #endif // CVMFS_CATALOG_H_ | ||
343 |