Directory: | cvmfs/ |
---|---|
File: | cvmfs/catalog.h |
Date: | 2025-02-09 02:34:19 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 46 | 51 | 90.2% |
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 | 351 | 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 | 41 | inline void MakeDummy() { offset = 1; } | |
60 | |||
61 | 2178 | inline bool IsInitialized() const { return offset > 0; } | |
62 |
3/4✓ Branch 1 taken 710 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
✓ Branch 4 taken 691 times.
|
710 | 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 | 66 | 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 | 536 | inline bool LookupPath(const PathString &path, DirectoryEntry *dirent) const { | |
125 |
1/2✓ Branch 2 taken 536 times.
✗ Branch 3 not taken.
|
536 | return LookupMd5Path(NormalizePath(path), dirent); |
126 | } | ||
127 | bool LookupRawSymlink(const PathString &path, LinkString *raw_symlink) const; | ||
128 | 1 | bool LookupXattrsPath(const PathString &path, XattrList *xattrs) const { | |
129 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | return LookupXattrsMd5Path(NormalizePath(path), xattrs); |
130 | } | ||
131 | |||
132 | 86 | inline bool ListingPath(const PathString &path, | |
133 | DirectoryEntryList *listing, | ||
134 | const bool expand_symlink = true) const | ||
135 | { | ||
136 |
1/2✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
|
86 | return ListingMd5Path(NormalizePath(path), listing, expand_symlink); |
137 | } | ||
138 | 5 | bool ListingPathStat(const PathString &path, | |
139 | StatEntryList *listing) const | ||
140 | { | ||
141 |
1/2✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | return ListingMd5PathStat(NormalizePath(path), listing); |
142 | } | ||
143 | bool AllChunksBegin(); | ||
144 | bool AllChunksNext(shash::Any *hash, zlib::Algorithms *compression_alg); | ||
145 | bool AllChunksEnd(); | ||
146 | |||
147 | 1 | inline bool ListPathChunks(const PathString &path, | |
148 | const shash::Algorithms interpret_hashes_as, | ||
149 | FileChunkList *chunks) const | ||
150 | { | ||
151 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | return ListMd5PathChunks(NormalizePath(path), interpret_hashes_as, chunks); |
152 | } | ||
153 | |||
154 | CatalogList GetChildren() const; | ||
155 | Catalog* FindSubtree(const PathString &path) const; | ||
156 | Catalog* FindChild(const PathString &mountpoint) const; | ||
157 | void AddChild(Catalog *child); | ||
158 | void RemoveChild(Catalog *child); | ||
159 | |||
160 | const HashVector& GetReferencedObjects() const; | ||
161 | void TakeDatabaseFileOwnership(); | ||
162 | void DropDatabaseFileOwnership(); | ||
163 | 5 | bool OwnsDatabaseFile() const { | |
164 |
5/6✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
|
5 | return ((database_ != NULL) && database_->OwnsFile()) || managed_database_; |
165 | } | ||
166 | |||
167 | uint64_t GetTTL() const; | ||
168 | bool HasExplicitTTL() const; | ||
169 | uint64_t GetRevision() const; | ||
170 | bool GetVOMSAuthz(std::string *authz) const; | ||
171 | uint64_t GetLastModified() const; | ||
172 | uint64_t GetNumEntries() const; | ||
173 | uint64_t GetNumChunks() const; | ||
174 | shash::Any GetPreviousRevision() const; | ||
175 | 42 | const Counters& GetCounters() const { return counters_; } | |
176 | std::string PrintMemStatistics() const; | ||
177 | |||
178 | 712 | inline float schema() const { return database().schema_version(); } | |
179 | 2168 | inline PathString mountpoint() const { return mountpoint_; } | |
180 | 139 | inline Catalog* parent() const { return parent_; } | |
181 | 103 | inline uint64_t max_row_id() const { return max_row_id_; } | |
182 | 102 | inline InodeRange inode_range() const { return inode_range_; } | |
183 | 143 | inline void set_inode_range(const InodeRange value) { inode_range_ = value; } | |
184 | 96 | inline std::string database_path() const { return database_->filename(); } | |
185 | 20 | inline PathString root_prefix() const { return root_prefix_; } | |
186 | 5 | inline shash::Any hash() const { return catalog_hash_; } | |
187 | 59 | inline bool volatile_flag() const { return volatile_flag_; } | |
188 | 5 | inline uint64_t revision() const { return GetRevision(); } | |
189 | |||
190 | 1468 | inline bool IsInitialized() const { | |
191 |
2/4✓ Branch 1 taken 1468 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1468 times.
✗ Branch 4 not taken.
|
1468 | return inode_range_.IsInitialized() && initialized_; |
192 | } | ||
193 | 159 | inline bool IsRoot() const { return is_root_; } | |
194 | ✗ | bool IsAutogenerated() const { | |
195 | ✗ | DirectoryEntry dirent; | |
196 | ✗ | assert(IsInitialized()); | |
197 | ✗ | return LookupPath(PathString( | |
198 | ✗ | mountpoint_.ToString() + "/.cvmfsautocatalog"), &dirent); | |
199 | } | ||
200 | 1015 | inline bool HasParent() const { return parent_ != NULL; } | |
201 | 2 | inline virtual bool IsWritable() const { return false; } | |
202 | |||
203 | typedef struct { | ||
204 | PathString mountpoint; | ||
205 | shash::Any hash; | ||
206 | uint64_t size; | ||
207 | } NestedCatalog; | ||
208 | typedef std::vector<NestedCatalog> NestedCatalogList; | ||
209 | const NestedCatalogList& ListNestedCatalogs() const; | ||
210 | const NestedCatalogList ListOwnNestedCatalogs() const; | ||
211 | bool FindNested(const PathString &mountpoint, | ||
212 | shash::Any *hash, uint64_t *size) const; | ||
213 | |||
214 | void SetInodeAnnotation(InodeAnnotation *new_annotation); | ||
215 | inode_t GetMangledInode(const uint64_t row_id, | ||
216 | const uint64_t hardlink_group) const; | ||
217 | |||
218 | void SetOwnerMaps(const OwnerMap *uid_map, const OwnerMap *gid_map); | ||
219 | 710 | uint64_t MapUid(const uint64_t uid) const { | |
220 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 710 times.
|
710 | if (uid_map_) { return uid_map_->Map(uid); } |
221 | 710 | return uid; | |
222 | } | ||
223 | 710 | uint64_t MapGid(const uint64_t gid) const { | |
224 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 710 times.
|
710 | if (gid_map_) { return gid_map_->Map(gid); } |
225 | 710 | 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 | 57 | virtual CatalogDatabase::OpenMode DatabaseOpenMode() const { | |
241 | 57 | return CatalogDatabase::kOpenReadOnly; | |
242 | } | ||
243 | |||
244 | virtual void InitPreparedStatements(); | ||
245 | void FinalizePreparedStatements(); | ||
246 | |||
247 | 1 | Counters& GetWritableCounters() { return counters_; } | |
248 | |||
249 | 1047 | inline const CatalogDatabase &database() const { return *database_; } | |
250 | 3856 | inline CatalogDatabase &database() { return *database_; } | |
251 | 99 | 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 |