Directory: | cvmfs/ |
---|---|
File: | cvmfs/catalog.cc |
Date: | 2025-06-22 02:36:02 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 407 | 438 | 92.9% |
Branches: | 324 | 606 | 53.5% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #include "catalog.h" | ||
6 | |||
7 | #include <alloca.h> | ||
8 | #include <errno.h> | ||
9 | |||
10 | #include <algorithm> | ||
11 | #include <cassert> | ||
12 | |||
13 | #include "catalog_mgr.h" | ||
14 | #include "util/concurrency.h" | ||
15 | #include "util/logging.h" | ||
16 | #include "util/platform.h" | ||
17 | #include "util/smalloc.h" | ||
18 | |||
19 | using namespace std; // NOLINT | ||
20 | |||
21 | namespace catalog { | ||
22 | |||
23 | const shash::Md5 Catalog::kMd5PathEmpty("", 0); | ||
24 | |||
25 | |||
26 | /** | ||
27 | * Open a catalog outside the framework of a catalog manager. | ||
28 | */ | ||
29 | 374 | Catalog *Catalog::AttachFreely(const string &imaginary_mountpoint, | |
30 | const string &file, | ||
31 | const shash::Any &catalog_hash, | ||
32 | Catalog *parent, | ||
33 | const bool is_nested) { | ||
34 | Catalog *catalog = new Catalog( | ||
35 | 374 | PathString(imaginary_mountpoint.data(), imaginary_mountpoint.length()), | |
36 | catalog_hash, | ||
37 | parent, | ||
38 |
2/4✓ Branch 1 taken 374 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 374 times.
✗ Branch 5 not taken.
|
374 | is_nested); |
39 | 374 | const bool successful_init = catalog->InitStandalone(file); | |
40 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 369 times.
|
374 | if (!successful_init) { |
41 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | delete catalog; |
42 | 5 | return NULL; | |
43 | } | ||
44 | 369 | return catalog; | |
45 | } | ||
46 | |||
47 | |||
48 | 4602 | Catalog::Catalog(const PathString &mountpoint, | |
49 | const shash::Any &catalog_hash, | ||
50 | Catalog *parent, | ||
51 | 4602 | const bool is_nested) | |
52 | 4602 | : catalog_hash_(catalog_hash) | |
53 |
1/2✓ Branch 1 taken 4602 times.
✗ Branch 2 not taken.
|
4602 | , mountpoint_(mountpoint) |
54 |
1/2✓ Branch 1 taken 4602 times.
✗ Branch 2 not taken.
|
4602 | , is_regular_mountpoint_(mountpoint_ == root_prefix_) |
55 | 4602 | , volatile_flag_(false) | |
56 |
3/4✓ Branch 0 taken 3077 times.
✓ Branch 1 taken 1525 times.
✓ Branch 2 taken 3077 times.
✗ Branch 3 not taken.
|
4602 | , is_root_(parent == NULL && !is_nested) |
57 | 4602 | , managed_database_(false) | |
58 | 4602 | , parent_(parent) | |
59 | 4602 | , nested_catalog_cache_dirty_(true) | |
60 | 4602 | , voms_authz_status_(kVomsUnknown) | |
61 |
1/2✓ Branch 8 taken 4602 times.
✗ Branch 9 not taken.
|
9204 | , initialized_(false) { |
62 | 4602 | max_row_id_ = 0; | |
63 | 4602 | inode_annotation_ = NULL; | |
64 | 4602 | lock_ = reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t))); | |
65 | 4602 | const int retval = pthread_mutex_init(lock_, NULL); | |
66 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4602 times.
|
4602 | assert(retval == 0); |
67 | |||
68 | 4602 | database_ = NULL; | |
69 | 4602 | uid_map_ = NULL; | |
70 | 4602 | gid_map_ = NULL; | |
71 | 4602 | sql_listing_ = NULL; | |
72 | 4602 | sql_lookup_md5path_ = NULL; | |
73 | 4602 | sql_lookup_nested_ = NULL; | |
74 | 4602 | sql_list_nested_ = NULL; | |
75 | 4602 | sql_own_list_nested_ = NULL; | |
76 | 4602 | sql_all_chunks_ = NULL; | |
77 | 4602 | sql_chunks_listing_ = NULL; | |
78 | 4602 | sql_lookup_xattrs_ = NULL; | |
79 | 4602 | } | |
80 | |||
81 | |||
82 | 13060 | Catalog::~Catalog() { | |
83 | 9166 | pthread_mutex_destroy(lock_); | |
84 | 9166 | free(lock_); | |
85 | 9166 | FinalizePreparedStatements(); | |
86 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
9166 | delete database_; |
87 | 13060 | } | |
88 | |||
89 | |||
90 | /** | ||
91 | * InitPreparedStatement uses polymorphism in case of a r/w catalog. | ||
92 | * FinalizePreparedStatements is called in the destructor where | ||
93 | * polymorphism does not work any more and has to be called both in | ||
94 | * the WritableCatalog and the Catalog destructor | ||
95 | */ | ||
96 | 4548 | void Catalog::InitPreparedStatements() { | |
97 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_listing_ = new SqlListing(database()); |
98 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_lookup_md5path_ = new SqlLookupPathHash(database()); |
99 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_lookup_nested_ = new SqlNestedCatalogLookup(database()); |
100 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_list_nested_ = new SqlNestedCatalogListing(database()); |
101 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_own_list_nested_ = new SqlOwnNestedCatalogListing(database()); |
102 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_all_chunks_ = new SqlAllChunks(database()); |
103 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_chunks_listing_ = new SqlChunksListing(database()); |
104 |
1/2✓ Branch 3 taken 4548 times.
✗ Branch 4 not taken.
|
4548 | sql_lookup_xattrs_ = new SqlLookupXattrs(database()); |
105 | 4548 | } | |
106 | |||
107 | |||
108 | 4583 | void Catalog::FinalizePreparedStatements() { | |
109 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_lookup_xattrs_; |
110 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_chunks_listing_; |
111 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_all_chunks_; |
112 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_listing_; |
113 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_lookup_md5path_; |
114 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_lookup_nested_; |
115 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_list_nested_; |
116 |
2/2✓ Branch 0 taken 4531 times.
✓ Branch 1 taken 52 times.
|
4583 | delete sql_own_list_nested_; |
117 | 4583 | } | |
118 | |||
119 | |||
120 | 524 | bool Catalog::InitStandalone(const std::string &database_file) { | |
121 |
1/2✓ Branch 1 taken 524 times.
✗ Branch 2 not taken.
|
524 | const bool retval = OpenDatabase(database_file); |
122 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 519 times.
|
524 | if (!retval) { |
123 | 5 | return false; | |
124 | } | ||
125 | |||
126 | 519 | InodeRange inode_range; | |
127 | 519 | inode_range.MakeDummy(); | |
128 | 519 | set_inode_range(inode_range); | |
129 | 519 | return true; | |
130 | } | ||
131 | |||
132 | |||
133 | 7331 | bool Catalog::ReadCatalogCounters() { | |
134 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7331 times.
|
7331 | assert(database_ != NULL); |
135 | bool statistics_loaded; | ||
136 | 7331 | if (database().schema_version() < CatalogDatabase::kLatestSupportedSchema | |
137 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7317 times.
|
7331 | - CatalogDatabase::kSchemaEpsilon) { |
138 | 14 | statistics_loaded = counters_.ReadFromDatabase(database(), | |
139 | LegacyMode::kLegacy); | ||
140 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 7317 times.
|
7317 | } else if (database().schema_revision() < 2) { |
141 | ✗ | statistics_loaded = counters_.ReadFromDatabase(database(), | |
142 | LegacyMode::kNoXattrs); | ||
143 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 7317 times.
|
7317 | } else if (database().schema_revision() < 3) { |
144 | ✗ | statistics_loaded = counters_.ReadFromDatabase(database(), | |
145 | LegacyMode::kNoExternals); | ||
146 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 7317 times.
|
7317 | } else if (database().schema_revision() < 5) { |
147 | ✗ | statistics_loaded = counters_.ReadFromDatabase(database(), | |
148 | LegacyMode::kNoSpecials); | ||
149 | } else { | ||
150 | 7317 | statistics_loaded = counters_.ReadFromDatabase(database()); | |
151 | } | ||
152 | 7331 | return statistics_loaded; | |
153 | } | ||
154 | |||
155 | |||
156 | /** | ||
157 | * Establishes the database structures and opens the sqlite database file. | ||
158 | * @param db_path the absolute path to the database file on local file system | ||
159 | * @return true on successful initialization otherwise false | ||
160 | */ | ||
161 | 4560 | bool Catalog::OpenDatabase(const string &db_path) { | |
162 |
2/4✓ Branch 1 taken 4560 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4560 times.
✗ Branch 5 not taken.
|
4560 | database_ = CatalogDatabase::Open(db_path, DatabaseOpenMode()); |
163 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4548 times.
|
4560 | if (NULL == database_) { |
164 | 12 | return false; | |
165 | } | ||
166 | |||
167 |
2/2✓ Branch 2 taken 14 times.
✓ Branch 3 taken 4534 times.
|
4548 | if (database_->IsEqualSchema(database_->schema_version(), 1.0)) { |
168 | // Possible fix-up for database layout lacking the content hash of | ||
169 | // nested catalogs | ||
170 | SqlCatalog sql_has_nested_sha1( | ||
171 | 14 | database(), | |
172 | "SELECT count(*) FROM sqlite_master " | ||
173 |
2/4✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
|
42 | "WHERE type='table' AND name='nested_catalogs' AND sql LIKE '%sha1%';"); |
174 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | const bool retval = sql_has_nested_sha1.FetchRow(); |
175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == true); |
176 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | const bool has_nested_sha1 = sql_has_nested_sha1.RetrieveInt64(0); |
177 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (!has_nested_sha1) { |
178 | 7 | database_->EnforceSchema(0.9, 0); | |
179 | } | ||
180 | 14 | } | |
181 | |||
182 |
1/2✓ Branch 1 taken 4548 times.
✗ Branch 2 not taken.
|
4548 | InitPreparedStatements(); |
183 | |||
184 | // Set the database file ownership if requested | ||
185 |
2/2✓ Branch 0 taken 252 times.
✓ Branch 1 taken 4296 times.
|
4548 | if (managed_database_) { |
186 |
1/2✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
|
252 | database_->TakeFileOwnership(); |
187 | } | ||
188 | |||
189 | // Find out the maximum row id of this database file | ||
190 |
2/4✓ Branch 2 taken 4548 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 4548 times.
✗ Branch 7 not taken.
|
9096 | SqlCatalog sql_max_row_id(database(), "SELECT MAX(rowid) FROM catalog;"); |
191 |
2/4✓ Branch 1 taken 4548 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4548 times.
|
4548 | if (!sql_max_row_id.FetchRow()) { |
192 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, | |
193 | "Cannot retrieve maximal row id for database file %s " | ||
194 | "(SqliteErrorcode: %d)", | ||
195 | db_path.c_str(), sql_max_row_id.GetLastError()); | ||
196 | ✗ | return false; | |
197 | } | ||
198 |
1/2✓ Branch 1 taken 4548 times.
✗ Branch 2 not taken.
|
4548 | max_row_id_ = sql_max_row_id.RetrieveInt64(0); |
199 | |||
200 | // Get root prefix | ||
201 |
4/6✓ Branch 2 taken 4548 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4548 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1669 times.
✓ Branch 10 taken 2879 times.
|
4548 | if (database_->HasProperty("root_prefix")) { |
202 | 1669 | const std::string root_prefix = database_->GetProperty<std::string>( | |
203 |
2/4✓ Branch 2 taken 1669 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1669 times.
✗ Branch 6 not taken.
|
3338 | "root_prefix"); |
204 |
1/2✓ Branch 3 taken 1669 times.
✗ Branch 4 not taken.
|
1669 | root_prefix_.Assign(root_prefix.data(), root_prefix.size()); |
205 |
1/2✓ Branch 3 taken 1669 times.
✗ Branch 4 not taken.
|
1669 | LogCvmfs(kLogCatalog, kLogDebug, |
206 | "found root prefix %s in root catalog file %s", | ||
207 | root_prefix_.c_str(), db_path.c_str()); | ||
208 |
1/2✓ Branch 1 taken 1669 times.
✗ Branch 2 not taken.
|
1669 | is_regular_mountpoint_ = (root_prefix_ == mountpoint_); |
209 | 1669 | } else { | |
210 |
1/2✓ Branch 2 taken 2879 times.
✗ Branch 3 not taken.
|
2879 | LogCvmfs(kLogCatalog, kLogDebug, "no root prefix for root catalog file %s", |
211 | db_path.c_str()); | ||
212 | } | ||
213 | |||
214 | // Get volatile content flag | ||
215 |
2/4✓ Branch 1 taken 4548 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4548 times.
✗ Branch 5 not taken.
|
4548 | volatile_flag_ = database_->GetPropertyDefault<bool>("volatile", |
216 | 4548 | volatile_flag_); | |
217 | |||
218 | // Read Catalog Counter Statistics | ||
219 |
2/4✓ Branch 1 taken 4548 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4548 times.
|
4548 | if (!ReadCatalogCounters()) { |
220 | ✗ | LogCvmfs(kLogCatalog, kLogStderr, | |
221 | "failed to load statistics counters for catalog %s (file %s)", | ||
222 | mountpoint_.c_str(), db_path.c_str()); | ||
223 | ✗ | return false; | |
224 | } | ||
225 | |||
226 |
2/2✓ Branch 1 taken 1525 times.
✓ Branch 2 taken 3023 times.
|
4548 | if (HasParent()) { |
227 |
1/2✓ Branch 1 taken 1525 times.
✗ Branch 2 not taken.
|
1525 | parent_->AddChild(this); |
228 | } | ||
229 | |||
230 | 4548 | initialized_ = true; | |
231 | 4548 | return true; | |
232 | 4548 | } | |
233 | |||
234 | |||
235 | /** | ||
236 | * Removes the mountpoint and prepends the root prefix to path | ||
237 | */ | ||
238 | 22571 | shash::Md5 Catalog::NormalizePath(const PathString &path) const { | |
239 |
2/2✓ Branch 0 taken 22487 times.
✓ Branch 1 taken 84 times.
|
22571 | if (is_regular_mountpoint_) |
240 |
1/2✓ Branch 3 taken 22487 times.
✗ Branch 4 not taken.
|
22487 | return shash::Md5(path.GetChars(), path.GetLength()); |
241 | |||
242 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
|
84 | assert(path.GetLength() >= mountpoint_.GetLength()); |
243 | // Piecewise hash calculation: root_prefix plus tail of path | ||
244 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | shash::Any result(shash::kMd5); |
245 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | shash::ContextPtr ctx(shash::kMd5); |
246 | 84 | ctx.buffer = alloca(ctx.size); | |
247 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | shash::Init(ctx); |
248 |
1/2✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
|
168 | shash::Update( |
249 | 84 | reinterpret_cast<const unsigned char *>(root_prefix_.GetChars()), | |
250 | root_prefix_.GetLength(), | ||
251 | ctx); | ||
252 | 84 | shash::Update(reinterpret_cast<const unsigned char *>(path.GetChars()) | |
253 |
1/2✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
|
84 | + mountpoint_.GetLength(), |
254 | 84 | path.GetLength() - mountpoint_.GetLength(), | |
255 | ctx); | ||
256 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | shash::Final(ctx, &result); |
257 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | return result.CastToMd5(); |
258 | } | ||
259 | |||
260 | |||
261 | /** | ||
262 | * Same as NormalizePath but returns a PathString instead of an Md5 hash. | ||
263 | */ | ||
264 | 1666 | PathString Catalog::NormalizePath2(const PathString &path) const { | |
265 |
2/2✓ Branch 0 taken 1638 times.
✓ Branch 1 taken 28 times.
|
1666 | if (is_regular_mountpoint_) |
266 |
1/2✓ Branch 1 taken 1638 times.
✗ Branch 2 not taken.
|
1638 | return path; |
267 | |||
268 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
|
28 | assert(path.GetLength() >= mountpoint_.GetLength()); |
269 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | PathString result = root_prefix_; |
270 |
1/2✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
|
28 | const PathString suffix = path.Suffix(mountpoint_.GetLength()); |
271 |
1/2✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
|
28 | result.Append(suffix.GetChars(), suffix.GetLength()); |
272 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | return result; |
273 | 28 | } | |
274 | |||
275 | |||
276 | /** | ||
277 | * The opposite of NormalizePath: from a full path remove the root prefix and | ||
278 | * add the catalogs current mountpoint. Needed for normalized paths from the | ||
279 | * SQlite tables, such as nested catalog entry points. | ||
280 | */ | ||
281 | 1598 | PathString Catalog::PlantPath(const PathString &path) const { | |
282 |
2/2✓ Branch 0 taken 1570 times.
✓ Branch 1 taken 28 times.
|
1598 | if (is_regular_mountpoint_) |
283 |
1/2✓ Branch 1 taken 1570 times.
✗ Branch 2 not taken.
|
1570 | return path; |
284 | |||
285 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
|
28 | assert(path.GetLength() >= root_prefix_.GetLength()); |
286 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | PathString result = mountpoint_; |
287 |
1/2✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
|
28 | const PathString suffix = path.Suffix(root_prefix_.GetLength()); |
288 |
1/2✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
|
28 | result.Append(suffix.GetChars(), suffix.GetLength()); |
289 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | return result; |
290 | 28 | } | |
291 | |||
292 | |||
293 | /** | ||
294 | * Performs a lookup on this Catalog for a given MD5 path hash. | ||
295 | * @param md5path the MD5 hash of the searched path | ||
296 | * @param expand_symlink indicates if variables in symlink should be resolved | ||
297 | * @param dirent will be set to the found DirectoryEntry | ||
298 | * @return true if DirectoryEntry was successfully found, false otherwise | ||
299 | */ | ||
300 | 19927 | bool Catalog::LookupEntry(const shash::Md5 &md5path, const bool expand_symlink, | |
301 | DirectoryEntry *dirent) const { | ||
302 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 19927 times.
|
19927 | assert(IsInitialized()); |
303 | |||
304 | 19927 | const MutexLockGuard m(lock_); | |
305 |
1/2✓ Branch 1 taken 19927 times.
✗ Branch 2 not taken.
|
19927 | sql_lookup_md5path_->BindPathHash(md5path); |
306 |
1/2✓ Branch 1 taken 19927 times.
✗ Branch 2 not taken.
|
19927 | const bool found = sql_lookup_md5path_->FetchRow(); |
307 |
4/4✓ Branch 0 taken 19361 times.
✓ Branch 1 taken 566 times.
✓ Branch 2 taken 19346 times.
✓ Branch 3 taken 15 times.
|
19927 | if (found && (dirent != NULL)) { |
308 |
2/4✓ Branch 1 taken 19346 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19346 times.
✗ Branch 5 not taken.
|
19346 | *dirent = sql_lookup_md5path_->GetDirent(this, expand_symlink); |
309 |
1/2✓ Branch 1 taken 19346 times.
✗ Branch 2 not taken.
|
19346 | FixTransitionPoint(md5path, dirent); |
310 | } | ||
311 |
1/2✓ Branch 1 taken 19927 times.
✗ Branch 2 not taken.
|
19927 | sql_lookup_md5path_->Reset(); |
312 | |||
313 | 19927 | return found; | |
314 | 19927 | } | |
315 | |||
316 | |||
317 | /** | ||
318 | * Performs a lookup on this Catalog for a given MD5 path hash. | ||
319 | * @param md5path the MD5 hash of the searched path | ||
320 | * @param dirent will be set to the found DirectoryEntry | ||
321 | * @return true if DirectoryEntry was successfully found, false otherwise | ||
322 | */ | ||
323 | 19922 | bool Catalog::LookupMd5Path(const shash::Md5 &md5path, | |
324 | DirectoryEntry *dirent) const { | ||
325 | 19922 | return LookupEntry(md5path, true, dirent); | |
326 | } | ||
327 | |||
328 | |||
329 | 5 | bool Catalog::LookupRawSymlink(const PathString &path, | |
330 | LinkString *raw_symlink) const { | ||
331 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | DirectoryEntry dirent; |
332 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | const bool result = (LookupEntry(NormalizePath(path), false, &dirent)); |
333 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (result) |
334 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | raw_symlink->Assign(dirent.symlink()); |
335 | 5 | return result; | |
336 | 5 | } | |
337 | |||
338 | |||
339 | 5 | bool Catalog::LookupXattrsMd5Path(const shash::Md5 &md5path, | |
340 | XattrList *xattrs) const { | ||
341 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | assert(IsInitialized()); |
342 | |||
343 | 5 | const MutexLockGuard m(lock_); | |
344 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | sql_lookup_xattrs_->BindPathHash(md5path); |
345 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | const bool found = sql_lookup_xattrs_->FetchRow(); |
346 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | if (found && (xattrs != NULL)) { |
347 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | *xattrs = sql_lookup_xattrs_->GetXattrs(); |
348 | } | ||
349 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | sql_lookup_xattrs_->Reset(); |
350 | |||
351 | 5 | return found; | |
352 | 5 | } | |
353 | |||
354 | |||
355 | /** | ||
356 | * Perform a listing of the directory with the given MD5 path hash. | ||
357 | * @param path_hash the MD5 hash of the path of the directory to list | ||
358 | * @param listing will be set to the resulting DirectoryEntryList | ||
359 | * @return true on successful listing, false otherwise | ||
360 | */ | ||
361 | 117 | bool Catalog::ListingMd5PathStat(const shash::Md5 &md5path, | |
362 | StatEntryList *listing) const { | ||
363 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
|
117 | assert(IsInitialized()); |
364 | |||
365 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | DirectoryEntry dirent; |
366 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | StatEntry entry; |
367 | |||
368 | 117 | const MutexLockGuard m(lock_); | |
369 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | sql_listing_->BindPathHash(md5path); |
370 |
3/4✓ Branch 1 taken 447 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 330 times.
✓ Branch 4 taken 117 times.
|
447 | while (sql_listing_->FetchRow()) { |
371 |
2/4✓ Branch 1 taken 330 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 330 times.
✗ Branch 5 not taken.
|
330 | dirent = sql_listing_->GetDirent(this); |
372 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 323 times.
|
330 | if (dirent.IsHidden()) |
373 | 7 | continue; | |
374 |
1/2✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
|
323 | FixTransitionPoint(md5path, &dirent); |
375 |
2/4✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 323 times.
✗ Branch 5 not taken.
|
323 | entry.name = dirent.name(); |
376 |
1/2✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
|
323 | entry.info = dirent.GetStatStructure(); |
377 |
1/2✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
|
323 | listing->PushBack(entry); |
378 | } | ||
379 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | sql_listing_->Reset(); |
380 | |||
381 | 117 | return true; | |
382 | 117 | } | |
383 | |||
384 | |||
385 | /** | ||
386 | * Perform a listing of the directory with the given MD5 path hash. | ||
387 | * Returns only struct stat values | ||
388 | * @param path_hash the MD5 hash of the path of the directory to list | ||
389 | * @param listing will be set to the resulting DirectoryEntryList | ||
390 | * @param expand_symlink defines if magic symlinks should be resolved | ||
391 | * @return true on successful listing, false otherwise | ||
392 | */ | ||
393 | 3037 | bool Catalog::ListingMd5Path(const shash::Md5 &md5path, | |
394 | DirectoryEntryList *listing, | ||
395 | const bool expand_symlink) const { | ||
396 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3037 times.
|
3037 | assert(IsInitialized()); |
397 | |||
398 | 3037 | const MutexLockGuard m(lock_); | |
399 | |||
400 |
1/2✓ Branch 1 taken 3037 times.
✗ Branch 2 not taken.
|
3037 | sql_listing_->BindPathHash(md5path); |
401 |
3/4✓ Branch 1 taken 8434 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5397 times.
✓ Branch 4 taken 3037 times.
|
8434 | while (sql_listing_->FetchRow()) { |
402 |
1/2✓ Branch 1 taken 5397 times.
✗ Branch 2 not taken.
|
5397 | DirectoryEntry dirent = sql_listing_->GetDirent(this, expand_symlink); |
403 |
1/2✓ Branch 1 taken 5397 times.
✗ Branch 2 not taken.
|
5397 | FixTransitionPoint(md5path, &dirent); |
404 |
1/2✓ Branch 1 taken 5397 times.
✗ Branch 2 not taken.
|
5397 | listing->push_back(dirent); |
405 | 5397 | } | |
406 |
1/2✓ Branch 1 taken 3037 times.
✗ Branch 2 not taken.
|
3037 | sql_listing_->Reset(); |
407 | |||
408 | 3037 | return true; | |
409 | 3037 | } | |
410 | |||
411 | |||
412 | 14 | bool Catalog::AllChunksBegin() { return sql_all_chunks_->Open(); } | |
413 | |||
414 | |||
415 | 77 | bool Catalog::AllChunksNext(shash::Any *hash, | |
416 | zlib::Algorithms *compression_alg) { | ||
417 | 77 | return sql_all_chunks_->Next(hash, compression_alg); | |
418 | } | ||
419 | |||
420 | |||
421 | 14 | bool Catalog::AllChunksEnd() { return sql_all_chunks_->Close(); } | |
422 | |||
423 | |||
424 | /** | ||
425 | * Hash algorithm is given by the unchunked file. | ||
426 | * Could be figured out by a join but it is faster if the user of this | ||
427 | * method tells us. | ||
428 | */ | ||
429 | 7 | bool Catalog::ListMd5PathChunks(const shash::Md5 &md5path, | |
430 | const shash::Algorithms interpret_hashes_as, | ||
431 | FileChunkList *chunks) const { | ||
432 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
7 | assert(IsInitialized() && chunks->IsEmpty()); |
433 | |||
434 | 7 | const MutexLockGuard m(lock_); | |
435 | |||
436 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | sql_chunks_listing_->BindPathHash(md5path); |
437 |
3/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 7 times.
|
14 | while (sql_chunks_listing_->FetchRow()) { |
438 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
7 | chunks->PushBack(sql_chunks_listing_->GetFileChunk(interpret_hashes_as)); |
439 | } | ||
440 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | sql_chunks_listing_->Reset(); |
441 | |||
442 | 7 | return true; | |
443 | 7 | } | |
444 | |||
445 | |||
446 | /** | ||
447 | * Only used by the garbage collection | ||
448 | */ | ||
449 | 12 | const Catalog::HashVector &Catalog::GetReferencedObjects() const { | |
450 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
12 | if (!referenced_hashes_.empty()) { |
451 | ✗ | return referenced_hashes_; | |
452 | } | ||
453 | |||
454 | // retrieve all referenced content hashes of both files and file chunks | ||
455 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | SqlListContentHashes list_content_hashes(database()); |
456 |
3/4✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 75 times.
✓ Branch 4 taken 12 times.
|
87 | while (list_content_hashes.FetchRow()) { |
457 |
2/4✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 75 times.
✗ Branch 5 not taken.
|
75 | referenced_hashes_.push_back(list_content_hashes.GetHash()); |
458 | } | ||
459 | |||
460 | 12 | return referenced_hashes_; | |
461 | 12 | } | |
462 | |||
463 | |||
464 | 3051 | void Catalog::TakeDatabaseFileOwnership() { | |
465 | 3051 | managed_database_ = true; | |
466 |
2/2✓ Branch 0 taken 2799 times.
✓ Branch 1 taken 252 times.
|
3051 | if (NULL != database_) { |
467 | 2799 | database_->TakeFileOwnership(); | |
468 | } | ||
469 | 3051 | } | |
470 | |||
471 | |||
472 | 5 | void Catalog::DropDatabaseFileOwnership() { | |
473 | 5 | managed_database_ = false; | |
474 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (NULL != database_) { |
475 | 5 | database_->DropFileOwnership(); | |
476 | } | ||
477 | 5 | } | |
478 | |||
479 | |||
480 | 868 | uint64_t Catalog::GetTTL() const { | |
481 | 868 | const MutexLockGuard m(lock_); | |
482 |
2/4✓ Branch 3 taken 868 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 868 times.
✗ Branch 7 not taken.
|
1736 | return database().GetPropertyDefault<uint64_t>("TTL", kDefaultTTL); |
483 | 868 | } | |
484 | |||
485 | |||
486 | ✗ | bool Catalog::HasExplicitTTL() const { | |
487 | ✗ | const MutexLockGuard m(lock_); | |
488 | ✗ | return database().HasProperty("TTL"); | |
489 | } | ||
490 | |||
491 | |||
492 | 2570 | bool Catalog::GetVOMSAuthz(string *authz) const { | |
493 | bool result; | ||
494 | 2570 | const MutexLockGuard m(lock_); | |
495 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2570 times.
|
2570 | if (voms_authz_status_ == kVomsPresent) { |
496 | ✗ | if (authz) { | |
497 | ✗ | *authz = voms_authz_; | |
498 | } | ||
499 | ✗ | result = true; | |
500 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2570 times.
|
2570 | } else if (voms_authz_status_ == kVomsNone) { |
501 | ✗ | result = false; | |
502 | } else { | ||
503 |
3/6✓ Branch 3 taken 2570 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2570 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 2570 times.
|
2570 | if (database().HasProperty("voms_authz")) { |
504 | ✗ | voms_authz_ = database().GetProperty<string>("voms_authz"); | |
505 | ✗ | if (authz) { | |
506 | ✗ | *authz = voms_authz_; | |
507 | } | ||
508 | ✗ | voms_authz_status_ = kVomsPresent; | |
509 | } else { | ||
510 | 2570 | voms_authz_status_ = kVomsNone; | |
511 | } | ||
512 | 2570 | result = (voms_authz_status_ == kVomsPresent); | |
513 | } | ||
514 | 2570 | return result; | |
515 | 2570 | } | |
516 | |||
517 | 5366 | uint64_t Catalog::GetRevision() const { | |
518 | 5366 | const MutexLockGuard m(lock_); | |
519 |
2/4✓ Branch 3 taken 5366 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5366 times.
✗ Branch 7 not taken.
|
10732 | return database().GetPropertyDefault<uint64_t>("revision", 0); |
520 | 5366 | } | |
521 | |||
522 | 2584 | uint64_t Catalog::GetLastModified() const { | |
523 |
1/2✓ Branch 2 taken 2584 times.
✗ Branch 3 not taken.
|
2584 | const std::string prop_name = "last_modified"; |
524 |
1/2✓ Branch 2 taken 2584 times.
✗ Branch 3 not taken.
|
2584 | return (database().HasProperty(prop_name)) |
525 |
2/4✓ Branch 0 taken 2584 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 2584 times.
✗ Branch 5 not taken.
|
2584 | ? database().GetProperty<int>(prop_name) |
526 | 2584 | : 0u; | |
527 | 2584 | } | |
528 | |||
529 | |||
530 | ✗ | uint64_t Catalog::GetNumChunks() const { | |
531 | ✗ | return counters_.Get("self_regular") + counters_.Get("self_chunks"); | |
532 | } | ||
533 | |||
534 | |||
535 | 10 | uint64_t Catalog::GetNumEntries() const { | |
536 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | const string sql = "SELECT count(*) FROM catalog;"; |
537 | |||
538 | 10 | const MutexLockGuard m(lock_); | |
539 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | SqlCatalog stmt(database(), sql); |
540 |
3/6✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
|
20 | return (stmt.FetchRow()) ? stmt.RetrieveInt64(0) : 0; |
541 | 10 | } | |
542 | |||
543 | |||
544 | 12 | shash::Any Catalog::GetPreviousRevision() const { | |
545 | 12 | const MutexLockGuard m(lock_); | |
546 | 12 | const std::string hash_string = database().GetPropertyDefault<std::string>( | |
547 |
3/6✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
|
24 | "previous_revision", ""); |
548 | 12 | return (!hash_string.empty()) | |
549 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | ? shash::MkFromHexPtr(shash::HexPtr(hash_string), |
550 | shash::kSuffixCatalog) | ||
551 |
3/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
|
24 | : shash::Any(); |
552 | 12 | } | |
553 | |||
554 | |||
555 | 7 | string Catalog::PrintMemStatistics() const { | |
556 | 7 | sqlite::MemStatistics stats; | |
557 | { | ||
558 | 7 | const MutexLockGuard m(lock_); | |
559 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | database().GetMemStatistics(&stats); |
560 | 7 | } | |
561 |
4/8✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 7 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 7 times.
✗ Branch 14 not taken.
|
14 | return string(mountpoint().GetChars(), mountpoint().GetLength()) + ": " |
562 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.lookaside_slots_used) + " / " |
563 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.lookaside_slots_max) + " slots -- " |
564 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.lookaside_hit) + " hits, " |
565 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.lookaside_miss_size) + " misses-size, " |
566 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.lookaside_miss_full) + " misses-full -- " |
567 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.page_cache_used / 1024) + " kB pages -- " |
568 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.page_cache_hit) + " hits, " |
569 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.page_cache_miss) + " misses -- " |
570 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.schema_used / 1024) + " kB schema -- " |
571 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
28 | + StringifyInt(stats.stmt_used / 1024) + " kB statements"; |
572 | } | ||
573 | |||
574 | |||
575 | /** | ||
576 | * Determine the actual inode of a DirectoryEntry. | ||
577 | * The first used entry from a hardlink group deterimines the inode of the | ||
578 | * others. | ||
579 | * @param row_id the row id of a read row in the sqlite database | ||
580 | * @param hardlink_group the id of a possibly present hardlink group | ||
581 | * @return the assigned inode number | ||
582 | */ | ||
583 | 25073 | inode_t Catalog::GetMangledInode(const uint64_t row_id, | |
584 | const uint64_t hardlink_group) const { | ||
585 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 25073 times.
|
25073 | assert(IsInitialized()); |
586 | |||
587 |
2/2✓ Branch 1 taken 178 times.
✓ Branch 2 taken 24895 times.
|
25073 | if (inode_range_.IsDummy()) { |
588 | 178 | return DirectoryEntry::kInvalidInode; | |
589 | } | ||
590 | |||
591 | 24895 | inode_t inode = row_id + inode_range_.offset; | |
592 | |||
593 | // Hardlinks are encoded in catalog-wide unique hard link group ids. | ||
594 | // These ids must be resolved to actual inode relationships at runtime. | ||
595 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24895 times.
|
24895 | if (hardlink_group > 0) { |
596 | const HardlinkGroupMap::const_iterator inode_iter = | ||
597 | ✗ | hardlink_groups_.find(hardlink_group); | |
598 | |||
599 | // Use cached entry if possible | ||
600 | ✗ | if (inode_iter == hardlink_groups_.end()) { | |
601 | ✗ | hardlink_groups_[hardlink_group] = inode; | |
602 | } else { | ||
603 | ✗ | inode = inode_iter->second; | |
604 | } | ||
605 | } | ||
606 | |||
607 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24895 times.
|
24895 | if (inode_annotation_) { |
608 | ✗ | inode = inode_annotation_->Annotate(inode); | |
609 | } | ||
610 | |||
611 | 24895 | return inode; | |
612 | } | ||
613 | |||
614 | |||
615 | /** | ||
616 | * Get a list of all registered nested catalogs and bind mountpoints in this | ||
617 | * catalog. | ||
618 | * @return a list of all nested catalog and bind mountpoints. | ||
619 | */ | ||
620 | 14752 | const Catalog::NestedCatalogList &Catalog::ListNestedCatalogs() const { | |
621 | 14752 | const MutexLockGuard m(lock_); | |
622 | |||
623 |
2/2✓ Branch 0 taken 3036 times.
✓ Branch 1 taken 11716 times.
|
14752 | if (nested_catalog_cache_dirty_) { |
624 |
1/2✓ Branch 2 taken 3036 times.
✗ Branch 3 not taken.
|
3036 | LogCvmfs(kLogCatalog, kLogDebug, "refreshing nested catalog cache of '%s'", |
625 |
1/2✓ Branch 1 taken 3036 times.
✗ Branch 2 not taken.
|
6072 | mountpoint().c_str()); |
626 |
3/4✓ Branch 1 taken 4393 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1357 times.
✓ Branch 4 taken 3036 times.
|
4393 | while (sql_list_nested_->FetchRow()) { |
627 |
1/2✓ Branch 1 taken 1357 times.
✗ Branch 2 not taken.
|
1357 | NestedCatalog nested; |
628 |
3/6✓ Branch 1 taken 1357 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1357 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1357 times.
✗ Branch 8 not taken.
|
1357 | nested.mountpoint = PlantPath(sql_list_nested_->GetPath()); |
629 |
1/2✓ Branch 1 taken 1357 times.
✗ Branch 2 not taken.
|
1357 | nested.hash = sql_list_nested_->GetContentHash(); |
630 |
1/2✓ Branch 1 taken 1357 times.
✗ Branch 2 not taken.
|
1357 | nested.size = sql_list_nested_->GetSize(); |
631 |
1/2✓ Branch 1 taken 1357 times.
✗ Branch 2 not taken.
|
1357 | nested_catalog_cache_.push_back(nested); |
632 | 1357 | } | |
633 |
1/2✓ Branch 1 taken 3036 times.
✗ Branch 2 not taken.
|
3036 | sql_list_nested_->Reset(); |
634 | 3036 | nested_catalog_cache_dirty_ = false; | |
635 | } | ||
636 | |||
637 | 14752 | return nested_catalog_cache_; | |
638 | 14752 | } | |
639 | |||
640 | |||
641 | /** | ||
642 | * Get a list of all registered nested catalogs without bind mountpoints. Used | ||
643 | * for replication and garbage collection. | ||
644 | * @return a list of all nested catalogs. | ||
645 | */ | ||
646 | 1170 | const Catalog::NestedCatalogList Catalog::ListOwnNestedCatalogs() const { | |
647 | 1170 | NestedCatalogList result; | |
648 | |||
649 | 1170 | const MutexLockGuard m(lock_); | |
650 | |||
651 |
3/4✓ Branch 1 taken 1369 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 199 times.
✓ Branch 4 taken 1170 times.
|
1369 | while (sql_own_list_nested_->FetchRow()) { |
652 |
1/2✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
|
199 | NestedCatalog nested; |
653 |
3/6✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 199 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 199 times.
✗ Branch 8 not taken.
|
199 | nested.mountpoint = PlantPath(sql_own_list_nested_->GetPath()); |
654 |
1/2✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
|
199 | nested.hash = sql_own_list_nested_->GetContentHash(); |
655 |
1/2✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
|
199 | nested.size = sql_own_list_nested_->GetSize(); |
656 |
1/2✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
|
199 | result.push_back(nested); |
657 | 199 | } | |
658 |
1/2✓ Branch 1 taken 1170 times.
✗ Branch 2 not taken.
|
1170 | sql_own_list_nested_->Reset(); |
659 | |||
660 | 2340 | return result; | |
661 | 1170 | } | |
662 | |||
663 | |||
664 | /** | ||
665 | * Drops the nested catalog cache. Usually this is only useful in subclasses | ||
666 | * that implement writable catalogs. | ||
667 | * | ||
668 | * Note: this action is _not_ secured by the catalog's mutex. If serialisation | ||
669 | * is required the subclass needs to ensure that. | ||
670 | */ | ||
671 | 2166 | void Catalog::ResetNestedCatalogCacheUnprotected() { | |
672 | 2166 | nested_catalog_cache_.clear(); | |
673 | 2166 | nested_catalog_cache_dirty_ = true; | |
674 | 2166 | } | |
675 | |||
676 | |||
677 | /** | ||
678 | * Looks for a specific registered nested catalog based on a path. | ||
679 | */ | ||
680 | 1624 | bool Catalog::FindNested(const PathString &mountpoint, shash::Any *hash, | |
681 | uint64_t *size) const { | ||
682 | 1624 | const MutexLockGuard m(lock_); | |
683 |
1/2✓ Branch 1 taken 1624 times.
✗ Branch 2 not taken.
|
1624 | const PathString normalized_mountpoint = NormalizePath2(mountpoint); |
684 |
1/2✓ Branch 1 taken 1624 times.
✗ Branch 2 not taken.
|
1624 | sql_lookup_nested_->BindSearchPath(normalized_mountpoint); |
685 |
1/2✓ Branch 1 taken 1624 times.
✗ Branch 2 not taken.
|
1624 | const bool found = sql_lookup_nested_->FetchRow(); |
686 |
3/4✓ Branch 0 taken 1602 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 1602 times.
✗ Branch 3 not taken.
|
1624 | if (found && (hash != NULL)) { |
687 |
1/2✓ Branch 1 taken 1602 times.
✗ Branch 2 not taken.
|
1602 | *hash = sql_lookup_nested_->GetContentHash(); |
688 |
1/2✓ Branch 1 taken 1602 times.
✗ Branch 2 not taken.
|
1602 | *size = sql_lookup_nested_->GetSize(); |
689 | } | ||
690 |
1/2✓ Branch 1 taken 1624 times.
✗ Branch 2 not taken.
|
1624 | sql_lookup_nested_->Reset(); |
691 | |||
692 | 1624 | return found; | |
693 | 1624 | } | |
694 | |||
695 | |||
696 | /** | ||
697 | * Sets a new object to do inode annotations (or set to NULL) | ||
698 | * The annotation object is not owned by the catalog. | ||
699 | */ | ||
700 | 4022 | void Catalog::SetInodeAnnotation(InodeAnnotation *new_annotation) { | |
701 | 4022 | const MutexLockGuard m(lock_); | |
702 | // Since annotated inodes could come back to the catalog in order to | ||
703 | // get stripped, exchanging the annotation is not allowed | ||
704 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 4022 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
4022 | assert((inode_annotation_ == NULL) || (inode_annotation_ == new_annotation)); |
705 | 4022 | inode_annotation_ = new_annotation; | |
706 | 4022 | } | |
707 | |||
708 | |||
709 | 4022 | void Catalog::SetOwnerMaps(const OwnerMap *uid_map, const OwnerMap *gid_map) { | |
710 |
2/4✓ Branch 0 taken 4022 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4022 times.
|
4022 | uid_map_ = (uid_map && uid_map->HasEffect()) ? uid_map : NULL; |
711 |
2/4✓ Branch 0 taken 4022 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4022 times.
|
4022 | gid_map_ = (gid_map && gid_map->HasEffect()) ? gid_map : NULL; |
712 | 4022 | } | |
713 | |||
714 | |||
715 | /** | ||
716 | * Add a Catalog as child to this Catalog. | ||
717 | * @param child the Catalog to define as child | ||
718 | */ | ||
719 | 1525 | void Catalog::AddChild(Catalog *child) { | |
720 |
3/7✓ Branch 1 taken 1525 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1525 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1525 times.
|
1525 | assert(NULL == FindChild(child->mountpoint())); |
721 | |||
722 | 1525 | const MutexLockGuard m(lock_); | |
723 |
2/4✓ Branch 1 taken 1525 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1525 times.
✗ Branch 5 not taken.
|
1525 | children_[child->mountpoint()] = child; |
724 | 1525 | child->set_parent(this); | |
725 | 1525 | } | |
726 | |||
727 | |||
728 | /** | ||
729 | * Removes a Catalog from the children list of this Catalog | ||
730 | * @param child the Catalog to delete as child | ||
731 | */ | ||
732 | 1450 | void Catalog::RemoveChild(Catalog *child) { | |
733 |
3/7✓ Branch 1 taken 1450 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1450 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1450 times.
|
1450 | assert(NULL != FindChild(child->mountpoint())); |
734 | |||
735 | 1450 | const MutexLockGuard m(lock_); | |
736 | 1450 | child->set_parent(NULL); | |
737 |
2/4✓ Branch 1 taken 1450 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1450 times.
✗ Branch 5 not taken.
|
1450 | children_.erase(child->mountpoint()); |
738 | 1450 | } | |
739 | |||
740 | |||
741 | 5830 | CatalogList Catalog::GetChildren() const { | |
742 | 5830 | CatalogList result; | |
743 | |||
744 | 5830 | const MutexLockGuard m(lock_); | |
745 | 11660 | for (NestedCatalogMap::const_iterator i = children_.begin(), | |
746 | 5830 | iEnd = children_.end(); | |
747 |
2/2✓ Branch 1 taken 2310 times.
✓ Branch 2 taken 5830 times.
|
8140 | i != iEnd; |
748 | 2310 | ++i) { | |
749 |
1/2✓ Branch 2 taken 2310 times.
✗ Branch 3 not taken.
|
2310 | result.push_back(i->second); |
750 | } | ||
751 | |||
752 | 11660 | return result; | |
753 | 5830 | } | |
754 | |||
755 | |||
756 | /** | ||
757 | * Find the nested catalog that serves the given path. | ||
758 | * It might be possible that the path is in fact served by a child of the found | ||
759 | * nested catalog. | ||
760 | * @param path the path to find a best fitting catalog for | ||
761 | * @return a pointer to the best fitting child or NULL if it does not fit at all | ||
762 | */ | ||
763 | 15468 | Catalog *Catalog::FindSubtree(const PathString &path) const { | |
764 | // Check if this catalog fits the beginning of the path. | ||
765 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 15468 times.
|
15468 | if (!path.StartsWith(mountpoint_)) |
766 | ✗ | return NULL; | |
767 | |||
768 |
1/2✓ Branch 2 taken 15468 times.
✗ Branch 3 not taken.
|
15468 | PathString remaining(path.Suffix(mountpoint_.GetLength())); |
769 |
1/2✓ Branch 1 taken 15468 times.
✗ Branch 2 not taken.
|
15468 | remaining.Append("/", 1); |
770 | |||
771 | // now we recombine the path elements successively | ||
772 | // in order to find a child which serves a part of the path | ||
773 |
1/2✓ Branch 1 taken 15468 times.
✗ Branch 2 not taken.
|
15468 | PathString path_prefix(mountpoint_); |
774 | 15468 | Catalog *result = NULL; | |
775 | // Skip the first '/' | ||
776 |
1/2✓ Branch 1 taken 15468 times.
✗ Branch 2 not taken.
|
15468 | path_prefix.Append("/", 1); |
777 | 15468 | const char *c = remaining.GetChars() + 1; | |
778 |
2/2✓ Branch 1 taken 131895 times.
✓ Branch 2 taken 13378 times.
|
145273 | for (unsigned i = 1; i < remaining.GetLength(); ++i, ++c) { |
779 |
2/2✓ Branch 0 taken 29809 times.
✓ Branch 1 taken 102086 times.
|
131895 | if (*c == '/') { |
780 |
1/2✓ Branch 1 taken 29809 times.
✗ Branch 2 not taken.
|
29809 | result = FindChild(path_prefix); |
781 | |||
782 | // If we found a child serving a part of the path we can stop searching. | ||
783 | // Remaining sub path elements are possibly served by a grand child. | ||
784 |
2/2✓ Branch 0 taken 2090 times.
✓ Branch 1 taken 27719 times.
|
29809 | if (result != NULL) |
785 | 2090 | break; | |
786 | } | ||
787 |
1/2✓ Branch 1 taken 129805 times.
✗ Branch 2 not taken.
|
129805 | path_prefix.Append(c, 1); |
788 | } | ||
789 | |||
790 | 15468 | return result; | |
791 | 15468 | } | |
792 | |||
793 | |||
794 | /** | ||
795 | * Looks for a child catalog, which is a subset of all registered nested | ||
796 | * catalogs. | ||
797 | */ | ||
798 | 32992 | Catalog *Catalog::FindChild(const PathString &mountpoint) const { | |
799 | 32992 | NestedCatalogMap::const_iterator nested_iter; | |
800 | |||
801 | 32992 | const MutexLockGuard m(lock_); | |
802 |
1/2✓ Branch 1 taken 32992 times.
✗ Branch 2 not taken.
|
32992 | nested_iter = children_.find(mountpoint); |
803 |
2/2✓ Branch 2 taken 29358 times.
✓ Branch 3 taken 3634 times.
|
36626 | Catalog *result = (nested_iter == children_.end()) ? NULL |
804 | 3634 | : nested_iter->second; | |
805 | |||
806 | 32992 | return result; | |
807 | 32992 | } | |
808 | |||
809 | |||
810 | /** | ||
811 | * For the transition points for nested catalogs and bind mountpoints, the inode | ||
812 | * is ambiguous. It has to be set to the parent inode because nested catalogs | ||
813 | * are lazily loaded. | ||
814 | * @param md5path the MD5 hash of the entry to check | ||
815 | * @param dirent the DirectoryEntry to perform coherence fixes on | ||
816 | */ | ||
817 | 25066 | void Catalog::FixTransitionPoint(const shash::Md5 &md5path, | |
818 | DirectoryEntry *dirent) const { | ||
819 |
2/2✓ Branch 1 taken 19925 times.
✓ Branch 2 taken 5141 times.
|
25066 | if (!HasParent()) |
820 | 19925 | return; | |
821 | |||
822 |
2/2✓ Branch 1 taken 473 times.
✓ Branch 2 taken 4668 times.
|
5141 | if (dirent->IsNestedCatalogRoot()) { |
823 | // Normal nested catalog | ||
824 |
1/2✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
|
473 | DirectoryEntry parent_dirent; |
825 |
1/2✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
|
473 | const bool retval = parent_->LookupMd5Path(md5path, &parent_dirent); |
826 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 473 times.
|
473 | assert(retval); |
827 | 473 | dirent->set_inode(parent_dirent.inode()); | |
828 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 4668 times.
|
5141 | } else if (md5path == kMd5PathEmpty) { |
829 | // Bind mountpoint | ||
830 | ✗ | DirectoryEntry parent_dirent; | |
831 | ✗ | const bool retval = parent_->LookupPath(mountpoint_, &parent_dirent); | |
832 | ✗ | assert(retval); | |
833 | ✗ | dirent->set_inode(parent_dirent.inode()); | |
834 | } | ||
835 | } | ||
836 | |||
837 | } // namespace catalog | ||
838 |