Directory: | cvmfs/ |
---|---|
File: | cvmfs/catalog_mgr_impl.h |
Date: | 2025-03-09 02:34:28 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 419 | 566 | 74.0% |
Branches: | 295 | 654 | 45.1% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System | ||
3 | */ | ||
4 | |||
5 | // avoid clang-tidy false positives (at least starting with clang14) | ||
6 | //NOLINTBEGIN | ||
7 | |||
8 | #ifndef CVMFS_CATALOG_MGR_IMPL_H_ | ||
9 | #define CVMFS_CATALOG_MGR_IMPL_H_ | ||
10 | |||
11 | #ifndef __STDC_FORMAT_MACROS | ||
12 | #define __STDC_FORMAT_MACROS | ||
13 | #endif | ||
14 | |||
15 | |||
16 | |||
17 | #include <cassert> | ||
18 | #include <string> | ||
19 | #include <vector> | ||
20 | |||
21 | #include "shortstring.h" | ||
22 | #include "statistics.h" | ||
23 | #include "util/logging.h" | ||
24 | #include "xattr.h" | ||
25 | |||
26 | using namespace std; // NOLINT | ||
27 | |||
28 | namespace catalog { | ||
29 | |||
30 | template <class CatalogT> | ||
31 | 75 | AbstractCatalogManager<CatalogT>::AbstractCatalogManager( | |
32 | perf::Statistics *statistics) : | ||
33 |
1/2✓ Branch 4 taken 75 times.
✗ Branch 5 not taken.
|
75 | statistics_(statistics) { |
34 | 75 | inode_watermark_status_ = 0; | |
35 | 75 | inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset; | |
36 | 75 | revision_cache_ = 0; | |
37 | 75 | timestamp_cache_ = 0; | |
38 | 75 | catalog_watermark_ = 0; | |
39 | 75 | volatile_flag_ = false; | |
40 | 75 | has_authz_cache_ = false; | |
41 | 75 | inode_annotation_ = NULL; | |
42 | 75 | incarnation_ = 0; | |
43 | 75 | rwlock_ = | |
44 | 75 | reinterpret_cast<pthread_rwlock_t *>(smalloc(sizeof(pthread_rwlock_t))); | |
45 | 75 | int retval = pthread_rwlock_init(rwlock_, NULL); | |
46 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | assert(retval == 0); |
47 | 75 | retval = pthread_key_create(&pkey_sqlitemem_, NULL); | |
48 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | assert(retval == 0); |
49 | 75 | } | |
50 | |||
51 | template <class CatalogT> | ||
52 | 150 | AbstractCatalogManager<CatalogT>::~AbstractCatalogManager() { | |
53 | 150 | DetachAll(); | |
54 | 150 | pthread_key_delete(pkey_sqlitemem_); | |
55 | 150 | pthread_rwlock_destroy(rwlock_); | |
56 | 150 | free(rwlock_); | |
57 | } | ||
58 | |||
59 | template <class CatalogT> | ||
60 | 24 | void AbstractCatalogManager<CatalogT>::SetInodeAnnotation( | |
61 | InodeAnnotation *new_annotation) | ||
62 | { | ||
63 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
24 | assert(catalogs_.empty() || (new_annotation == inode_annotation_)); |
64 | 24 | inode_annotation_ = new_annotation; | |
65 | 24 | } | |
66 | |||
67 | template <class CatalogT> | ||
68 | 31 | void AbstractCatalogManager<CatalogT>::SetOwnerMaps(const OwnerMap &uid_map, | |
69 | const OwnerMap &gid_map) | ||
70 | { | ||
71 | 31 | uid_map_ = uid_map; | |
72 | 31 | gid_map_ = gid_map; | |
73 | 31 | } | |
74 | |||
75 | template <class CatalogT> | ||
76 | 24 | void AbstractCatalogManager<CatalogT>::SetCatalogWatermark(unsigned limit) { | |
77 | 24 | catalog_watermark_ = limit; | |
78 | 24 | } | |
79 | |||
80 | template <class CatalogT> | ||
81 | 133 | void AbstractCatalogManager<CatalogT>::CheckInodeWatermark() { | |
82 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
|
133 | if (inode_watermark_status_ > 0) |
83 | ✗ | return; | |
84 | |||
85 | 133 | uint64_t highest_inode = inode_gauge_; | |
86 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 115 times.
|
133 | if (inode_annotation_) |
87 | 18 | highest_inode += inode_annotation_->GetGeneration(); | |
88 | 133 | uint64_t uint32_border = 1; | |
89 | 133 | uint32_border = uint32_border << 32; | |
90 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 133 times.
|
133 | if (highest_inode >= uint32_border) { |
91 | ✗ | LogCvmfs(kLogCatalog, kLogDebug | kLogSyslogWarn, "inodes exceed 32bit"); | |
92 | ✗ | inode_watermark_status_++; | |
93 | } | ||
94 | } | ||
95 | |||
96 | |||
97 | /** | ||
98 | * Initializes the CatalogManager and loads and attaches the root entry. | ||
99 | * @return true on successful init, otherwise false | ||
100 | */ | ||
101 | template <class CatalogT> | ||
102 | 57 | bool AbstractCatalogManager<CatalogT>::Init() { | |
103 | 57 | LogCvmfs(kLogCatalog, kLogDebug, "Initialize catalog"); | |
104 | 57 | WriteLock(); | |
105 |
2/4✓ Branch 2 taken 57 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 57 times.
✗ Branch 6 not taken.
|
57 | bool attached = MountCatalog(PathString("", 0), shash::Any(), NULL); |
106 | 57 | Unlock(); | |
107 | |||
108 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 52 times.
|
57 | if (!attached) { |
109 | 5 | LogCvmfs(kLogCatalog, kLogDebug, "failed to initialize root catalog"); | |
110 | } | ||
111 | |||
112 | 57 | return attached; | |
113 | } | ||
114 | |||
115 | |||
116 | /** | ||
117 | * Remounts the root catalog if necessary. If a newer root catalog exists, | ||
118 | * it is mounted and replaces the currently mounted tree (all existing catalogs | ||
119 | * are detached) | ||
120 | */ | ||
121 | template <class CatalogT> | ||
122 | 3 | LoadReturn AbstractCatalogManager<CatalogT>::RemountDryrun() { | |
123 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | LogCvmfs(kLogCatalog, kLogDebug, |
124 | "dryrun remounting repositories"); | ||
125 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | CatalogContext ctlg_context; |
126 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | return GetNewRootCatalogContext(&ctlg_context); |
127 | 3 | } | |
128 | |||
129 | template <class CatalogT> | ||
130 | 3 | LoadReturn AbstractCatalogManager<CatalogT>::Remount() { | |
131 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | LogCvmfs(kLogCatalog, kLogDebug, "remounting repositories"); |
132 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | CatalogContext ctlg_context; |
133 | |||
134 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | if (GetNewRootCatalogContext(&ctlg_context) != kLoadNew |
135 |
5/8✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
|
3 | && GetNewRootCatalogContext(&ctlg_context) != kLoadUp2Date) { |
136 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, "remounting repositories: " | |
137 | "Did not find any valid root catalog to mount"); | ||
138 | ✗ | return kLoadFail; | |
139 | } | ||
140 | |||
141 | 3 | WriteLock(); | |
142 | |||
143 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | const LoadReturn load_error = LoadCatalogByHash(&ctlg_context); |
144 | |||
145 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (load_error == kLoadNew) { |
146 | 2 | inode_t old_inode_gauge = inode_gauge_; | |
147 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | DetachAll(); |
148 | 2 | inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset; | |
149 | |||
150 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | CatalogT *new_root = CreateCatalog(ctlg_context.mountpoint(), |
151 | 2 | ctlg_context.hash(), NULL); | |
152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | assert(new_root); |
153 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | bool retval = AttachCatalog(ctlg_context.sqlite_path(), new_root); |
154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | assert(retval); |
155 | |||
156 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (inode_annotation_) { |
157 | ✗ | inode_annotation_->IncGeneration(old_inode_gauge); | |
158 | } | ||
159 | } | ||
160 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | CheckInodeWatermark(); |
161 | 3 | Unlock(); | |
162 | |||
163 | 3 | return load_error; | |
164 | 3 | } | |
165 | |||
166 | /** | ||
167 | * Remounts to the given hash | ||
168 | */ | ||
169 | template <class CatalogT> | ||
170 | ✗ | LoadReturn AbstractCatalogManager<CatalogT>::ChangeRoot( | |
171 | const shash::Any &root_hash) | ||
172 | { | ||
173 | ✗ | assert(!root_hash.IsNull()); | |
174 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, | |
175 | "switching to root hash %s", root_hash.ToString().c_str()); | ||
176 | |||
177 | ✗ | WriteLock(); | |
178 | |||
179 | ✗ | CatalogContext ctlg_context(root_hash, PathString("", 0), | |
180 | kCtlgNoLocationNeeded); | ||
181 | // we do not need to set revision as LoadCatalogByHash | ||
182 | // needs only mountpoint, hash | ||
183 | |||
184 | ✗ | const LoadReturn load_error = LoadCatalogByHash(&ctlg_context); | |
185 | |||
186 | ✗ | if (load_error == kLoadNew) { | |
187 | ✗ | inode_t old_inode_gauge = inode_gauge_; | |
188 | ✗ | DetachAll(); | |
189 | ✗ | inode_gauge_ = AbstractCatalogManager<CatalogT>::kInodeOffset; | |
190 | |||
191 | CatalogT *new_root = | ||
192 | ✗ | CreateCatalog(PathString("", 0), ctlg_context.hash(), NULL); | |
193 | ✗ | assert(new_root); | |
194 | ✗ | bool retval = AttachCatalog(ctlg_context.sqlite_path(), new_root); | |
195 | ✗ | assert(retval); | |
196 | |||
197 | ✗ | if (inode_annotation_) { | |
198 | ✗ | inode_annotation_->IncGeneration(old_inode_gauge); | |
199 | } | ||
200 | } | ||
201 | ✗ | CheckInodeWatermark(); | |
202 | ✗ | Unlock(); | |
203 | |||
204 | ✗ | return load_error; | |
205 | } | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Detaches everything except the root catalog | ||
210 | */ | ||
211 | template <class CatalogT> | ||
212 | 5 | void AbstractCatalogManager<CatalogT>::DetachNested() { | |
213 | 5 | WriteLock(); | |
214 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | if (catalogs_.empty()) { |
215 | ✗ | Unlock(); | |
216 | ✗ | return; | |
217 | } | ||
218 | |||
219 | 5 | typename CatalogList::const_iterator i; | |
220 | 5 | typename CatalogList::const_iterator iend; | |
221 |
1/2✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | CatalogList catalogs_to_detach = GetRootCatalog()->GetChildren(); |
222 | 5 | for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end(); | |
223 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 5 times.
|
9 | i != iend; ++i) |
224 | { | ||
225 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | DetachSubtree(*i); |
226 | } | ||
227 | |||
228 | 5 | Unlock(); | |
229 | 5 | } | |
230 | |||
231 | |||
232 | /** | ||
233 | * Returns the NULL hash if the nested catalog is not found. | ||
234 | */ | ||
235 | template <class CatalogT> | ||
236 | 8 | shash::Any AbstractCatalogManager<CatalogT>::GetNestedCatalogHash( | |
237 | const PathString &mountpoint) | ||
238 | { | ||
239 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
|
8 | assert(!mountpoint.IsEmpty()); |
240 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | CatalogT *catalog = FindCatalog(mountpoint); |
241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(catalog != NULL); |
242 |
3/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 7 times.
|
8 | if (catalog->mountpoint() == mountpoint) { |
243 | 1 | catalog = catalog->parent(); | |
244 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(catalog != NULL); |
245 | } | ||
246 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | shash::Any result; |
247 | uint64_t size; | ||
248 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | catalog->FindNested(mountpoint, &result, &size); |
249 | 16 | return result; | |
250 | } | ||
251 | |||
252 | |||
253 | /** | ||
254 | * Perform a lookup for a specific DirectoryEntry in the catalogs. | ||
255 | * @param path the path to find in the catalogs | ||
256 | * @param options whether to perform another lookup to get the parent entry, | ||
257 | * too | ||
258 | * @param dirent the resulting DirectoryEntry, or special Negative entry | ||
259 | * Note: can be set to zero if the result is not important | ||
260 | * @return true if lookup succeeded otherwise false | ||
261 | */ | ||
262 | template <class CatalogT> | ||
263 | 57 | bool AbstractCatalogManager<CatalogT>::LookupPath(const PathString &path, | |
264 | const LookupOptions options, | ||
265 | DirectoryEntry *dirent) | ||
266 | { | ||
267 | // initialize as non-negative | ||
268 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
|
57 | assert(dirent); |
269 |
2/4✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
|
57 | *dirent = DirectoryEntry(); |
270 | |||
271 | // create a dummy negative directory entry | ||
272 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
57 | const DirectoryEntry dirent_negative = |
273 | DirectoryEntry(catalog::kDirentNegative); | ||
274 | |||
275 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
57 | EnforceSqliteMemLimit(); |
276 | 57 | ReadLock(); | |
277 | |||
278 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
57 | CatalogT *best_fit = FindCatalog(path); |
279 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
|
57 | assert(best_fit != NULL); |
280 | |||
281 | 57 | perf::Inc(statistics_.n_lookup_path); | |
282 |
2/4✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 57 times.
✗ Branch 7 not taken.
|
57 | LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in catalog: '%s'", |
283 | path.c_str(), best_fit->mountpoint().c_str()); | ||
284 |
1/2✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
|
57 | bool found = best_fit->LookupPath(path, dirent); |
285 | |||
286 | // Possibly in a nested catalog | ||
287 |
7/8✓ Branch 0 taken 18 times.
✓ Branch 1 taken 39 times.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 11 times.
✓ Branch 8 taken 46 times.
|
57 | if (!found && MountSubtree(path, best_fit, false /* is_listable */, NULL)) { |
288 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | LogCvmfs(kLogCatalog, kLogDebug, "looking up '%s' in a nested catalog", |
289 | path.c_str()); | ||
290 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | StageNestedCatalogAndUnlock(path, best_fit, false /* is_listable */); |
291 | 11 | WriteLock(); | |
292 | // Check again to avoid race | ||
293 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | best_fit = FindCatalog(path); |
294 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | assert(best_fit != NULL); |
295 | 11 | perf::Inc(statistics_.n_lookup_path); | |
296 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | found = best_fit->LookupPath(path, dirent); |
297 | |||
298 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (!found) { |
299 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | LogCvmfs(kLogCatalog, kLogDebug, |
300 | "entry not found, we may have to load nested catalogs"); | ||
301 | |||
302 | CatalogT *nested_catalog; | ||
303 | found = | ||
304 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | MountSubtree(path, best_fit, false /* is_listable */, &nested_catalog); |
305 | |||
306 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (!found) { |
307 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, | |
308 | "failed to load nested catalog for '%s'", path.c_str()); | ||
309 | ✗ | goto lookup_path_notfound; | |
310 | } | ||
311 | |||
312 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (nested_catalog != best_fit) { |
313 | 11 | perf::Inc(statistics_.n_lookup_path); | |
314 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | found = nested_catalog->LookupPath(path, dirent); |
315 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (!found) { |
316 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, | |
317 | "nested catalogs loaded but entry '%s' was still not found", | ||
318 | path.c_str()); | ||
319 | ✗ | if (dirent != NULL) *dirent = dirent_negative; | |
320 | ✗ | goto lookup_path_notfound; | |
321 | } else { | ||
322 | 11 | best_fit = nested_catalog; | |
323 | } | ||
324 | } else { | ||
325 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, "no nested catalog fits"); | |
326 | ✗ | if (dirent != NULL) *dirent = dirent_negative; | |
327 | ✗ | goto lookup_path_notfound; | |
328 | } | ||
329 | } | ||
330 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | assert(found); |
331 | } | ||
332 | // Not in a nested catalog (because no nested cataog fits), ENOENT | ||
333 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 50 times.
|
57 | if (!found) { |
334 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | LogCvmfs(kLogCatalog, kLogDebug, "ENOENT: '%s'", path.c_str()); |
335 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
|
7 | if (dirent != NULL) *dirent = dirent_negative; |
336 | 7 | goto lookup_path_notfound; | |
337 | } | ||
338 | |||
339 |
2/4✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 50 times.
✗ Branch 7 not taken.
|
50 | LogCvmfs(kLogCatalog, kLogDebug, "found entry '%s' in catalog '%s'", |
340 | path.c_str(), best_fit->mountpoint().c_str()); | ||
341 | |||
342 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
|
50 | if ((options & kLookupRawSymlink) == kLookupRawSymlink) { |
343 | ✗ | LinkString raw_symlink; | |
344 | ✗ | bool retval = best_fit->LookupRawSymlink(path, &raw_symlink); | |
345 | ✗ | assert(retval); // Must be true, we have just found the entry | |
346 | ✗ | dirent->set_symlink(raw_symlink); | |
347 | } | ||
348 | |||
349 | 50 | Unlock(); | |
350 | 50 | return true; | |
351 | |||
352 | 7 | lookup_path_notfound: | |
353 | 7 | Unlock(); | |
354 | // Includes both: ENOENT and not found due to I/O error | ||
355 | 7 | perf::Inc(statistics_.n_lookup_path_negative); | |
356 | 7 | return false; | |
357 | 57 | } | |
358 | |||
359 | |||
360 | /** | ||
361 | * Perform a lookup for Nested Catalog that serves this path. | ||
362 | * If the path specified is a catalog mountpoint the catalog at that point is | ||
363 | * mounted and returned. | ||
364 | * @param path the path to find in the catalogs | ||
365 | * @param mountpoint the path to the nested catalog found | ||
366 | * @param hash the hash of the nested catalog found | ||
367 | * @param size the size of the nested catalog, 0 for root. Root is not a | ||
368 | * nested catalog in the database. | ||
369 | * @return true if lookup succeeded otherwise false (available catalog failed | ||
370 | * to mount) | ||
371 | */ | ||
372 | template <class CatalogT> | ||
373 | 8 | bool AbstractCatalogManager<CatalogT>::LookupNested( | |
374 | const PathString &path, | ||
375 | PathString *mountpoint, | ||
376 | shash::Any *hash, | ||
377 | uint64_t *size | ||
378 | ) { | ||
379 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | EnforceSqliteMemLimit(); |
380 | 8 | bool result = false; | |
381 | 8 | ReadLock(); | |
382 | |||
383 | // Look past current path to mount up to intended location | ||
384 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | PathString catalog_path(path); |
385 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | catalog_path.Append("/.cvmfscatalog", 14); |
386 | |||
387 | // Find catalog, possibly load nested | ||
388 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | CatalogT *best_fit = FindCatalog(catalog_path); |
389 | 8 | CatalogT *catalog = best_fit; | |
390 |
3/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 6 times.
|
8 | if (MountSubtree(catalog_path, best_fit, false /* is_listable */, NULL)) { |
391 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | StageNestedCatalogAndUnlock(path, best_fit, false); |
392 | 2 | WriteLock(); | |
393 | // Check again to avoid race | ||
394 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | best_fit = FindCatalog(catalog_path); |
395 | result = | ||
396 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | MountSubtree(catalog_path, best_fit, false /* is_listable */, &catalog); |
397 | // Result is false if an available catalog failed to load (error happened) | ||
398 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!result) { |
399 | ✗ | Unlock(); | |
400 | ✗ | return false; | |
401 | } | ||
402 | } | ||
403 | |||
404 | // If the found catalog is the Root there is no parent to lookup | ||
405 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
|
8 | if (catalog->HasParent()) { |
406 |
2/4✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
|
7 | result = catalog->parent()->FindNested(catalog->root_prefix(), hash, size); |
407 | } | ||
408 | |||
409 | // Mountpoint now points to the found catalog | ||
410 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | mountpoint->Assign(catalog->root_prefix()); |
411 | |||
412 | // If the result is false, it means that no nested catalog was found for | ||
413 | // this path. As the root catalog does not have a Nested Catalog of | ||
414 | // itself, we manually set the values and leave the size as 0. | ||
415 | // TODO(nhazekam) Allow for Root Catalog to be returned | ||
416 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
|
8 | if (!result) { |
417 | 1 | *hash = GetRootCatalog()->hash(); | |
418 | 1 | *size = 0; | |
419 | 1 | result = true; | |
420 | } | ||
421 | |||
422 | 8 | Unlock(); | |
423 | 8 | return result; | |
424 | 8 | } | |
425 | |||
426 | |||
427 | /** | ||
428 | * Create a listing of the parents, catalog, and children of the catalog | ||
429 | * that serves the specified path. | ||
430 | * If the path specified is a catalog mountpoint the catalog at that point is | ||
431 | * mounted and returned. | ||
432 | * @param path the path to find in the catalogs | ||
433 | * @param result_list the list where the results will be added. | ||
434 | * @return true if the list could be created, false if catalog fails to mount. | ||
435 | */ | ||
436 | template <class CatalogT> | ||
437 | 2 | bool AbstractCatalogManager<CatalogT>::ListCatalogSkein( | |
438 | const PathString &path, | ||
439 | std::vector<PathString> *result_list | ||
440 | ) { | ||
441 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | EnforceSqliteMemLimit(); |
442 | bool result; | ||
443 | 2 | ReadLock(); | |
444 | |||
445 | // Look past current path to mount up to intended location | ||
446 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | PathString test(path); |
447 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | test.Append("/.cvmfscatalog", 14); |
448 | |||
449 | // Find catalog, possibly load nested | ||
450 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | CatalogT *best_fit = FindCatalog(test); |
451 | 2 | CatalogT *catalog = best_fit; | |
452 | // True if there is an available nested catalog | ||
453 |
3/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
|
2 | if (MountSubtree(test, best_fit, false /* is_listable */, NULL)) { |
454 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | StageNestedCatalogAndUnlock(path, best_fit, false); |
455 | 1 | WriteLock(); | |
456 | // Check again to avoid race | ||
457 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | best_fit = FindCatalog(test); |
458 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | result = MountSubtree(test, best_fit, false /* is_listable */, &catalog); |
459 | // result is false if an available catalog failed to load | ||
460 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!result) { |
461 | ✗ | Unlock(); | |
462 | ✗ | return false; | |
463 | } | ||
464 | } | ||
465 | |||
466 | // Build listing | ||
467 | 2 | CatalogT *cur_parent = catalog->parent(); | |
468 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (cur_parent) { |
469 | // Walk up parent tree to find base | ||
470 | 2 | std::vector<catalog::Catalog*> parents; | |
471 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
|
3 | while (cur_parent->HasParent()) { |
472 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | parents.push_back(cur_parent); |
473 | 1 | cur_parent = cur_parent->parent(); | |
474 | } | ||
475 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | parents.push_back(cur_parent); |
476 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
|
5 | while (!parents.empty()) { |
477 | // Add to list in order starting at root | ||
478 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
3 | result_list->push_back(parents.back()->root_prefix()); |
479 | 3 | parents.pop_back(); | |
480 | } | ||
481 | 2 | } | |
482 | // Add the current catalog | ||
483 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | result_list->push_back(catalog->root_prefix()); |
484 | |||
485 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | Catalog::NestedCatalogList children = catalog->ListOwnNestedCatalogs(); |
486 | |||
487 | // Add all children nested catalogs | ||
488 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
|
6 | for (unsigned i = 0; i < children.size(); i++) { |
489 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | result_list->push_back(children.at(i).mountpoint); |
490 | } | ||
491 | |||
492 | 2 | Unlock(); | |
493 | 2 | return true; | |
494 | 2 | } | |
495 | |||
496 | |||
497 | template <class CatalogT> | ||
498 | ✗ | bool AbstractCatalogManager<CatalogT>::LookupXattrs( | |
499 | const PathString &path, | ||
500 | XattrList *xattrs) | ||
501 | { | ||
502 | ✗ | EnforceSqliteMemLimit(); | |
503 | bool result; | ||
504 | ✗ | ReadLock(); | |
505 | |||
506 | // Find catalog, possibly load nested | ||
507 | ✗ | CatalogT *best_fit = FindCatalog(path); | |
508 | ✗ | CatalogT *catalog = best_fit; | |
509 | ✗ | if (MountSubtree(path, best_fit, false /* is_listable */, NULL)) { | |
510 | ✗ | StageNestedCatalogAndUnlock(path, best_fit, false); | |
511 | ✗ | WriteLock(); | |
512 | // Check again to avoid race | ||
513 | ✗ | best_fit = FindCatalog(path); | |
514 | ✗ | result = MountSubtree(path, best_fit, false /* is_listable */, &catalog); | |
515 | ✗ | if (!result) { | |
516 | ✗ | Unlock(); | |
517 | ✗ | return false; | |
518 | } | ||
519 | } | ||
520 | |||
521 | ✗ | perf::Inc(statistics_.n_lookup_xattrs); | |
522 | ✗ | result = catalog->LookupXattrsPath(path, xattrs); | |
523 | |||
524 | ✗ | Unlock(); | |
525 | ✗ | return result; | |
526 | } | ||
527 | |||
528 | |||
529 | /** | ||
530 | * Do a listing of the specified directory. | ||
531 | * @param path the path of the directory to list | ||
532 | * @param listing the resulting DirectoryEntryList | ||
533 | * @return true if listing succeeded otherwise false | ||
534 | */ | ||
535 | template <class CatalogT> | ||
536 | 48 | bool AbstractCatalogManager<CatalogT>::Listing(const PathString &path, | |
537 | DirectoryEntryList *listing, | ||
538 | const bool expand_symlink) | ||
539 | { | ||
540 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | EnforceSqliteMemLimit(); |
541 | bool result; | ||
542 | 48 | ReadLock(); | |
543 | |||
544 | // Find catalog, possibly load nested | ||
545 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | CatalogT *best_fit = FindCatalog(path); |
546 | 48 | CatalogT *catalog = best_fit; | |
547 |
3/4✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 38 times.
|
48 | if (MountSubtree(path, best_fit, true /* is_listable */, NULL)) { |
548 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | StageNestedCatalogAndUnlock(path, best_fit, true /* is_listable */); |
549 | 10 | WriteLock(); | |
550 | // Check again to avoid race | ||
551 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | best_fit = FindCatalog(path); |
552 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | result = MountSubtree(path, best_fit, true /* is_listable */, &catalog); |
553 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (!result) { |
554 | ✗ | Unlock(); | |
555 | ✗ | return false; | |
556 | } | ||
557 | } | ||
558 | |||
559 | 48 | perf::Inc(statistics_.n_listing); | |
560 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | result = catalog->ListingPath(path, listing, expand_symlink); |
561 | |||
562 | 48 | Unlock(); | |
563 | 48 | return result; | |
564 | } | ||
565 | |||
566 | |||
567 | /** | ||
568 | * Do a listing of the specified directory, return only struct stat values. | ||
569 | * @param path the path of the directory to list | ||
570 | * @param listing the resulting StatEntryList | ||
571 | * @return true if listing succeeded otherwise false | ||
572 | */ | ||
573 | template <class CatalogT> | ||
574 | 2 | bool AbstractCatalogManager<CatalogT>::ListingStat(const PathString &path, | |
575 | StatEntryList *listing) | ||
576 | { | ||
577 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | EnforceSqliteMemLimit(); |
578 | bool result; | ||
579 | 2 | ReadLock(); | |
580 | |||
581 | // Find catalog, possibly load nested | ||
582 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | CatalogT *best_fit = FindCatalog(path); |
583 | 2 | CatalogT *catalog = best_fit; | |
584 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
|
2 | if (MountSubtree(path, best_fit, true /* is_listable */, NULL)) { |
585 | ✗ | StageNestedCatalogAndUnlock(path, best_fit, true /* is_listable */); | |
586 | ✗ | WriteLock(); | |
587 | // Check again to avoid race | ||
588 | ✗ | best_fit = FindCatalog(path); | |
589 | ✗ | result = MountSubtree(path, best_fit, true /* is_listable */, &catalog); | |
590 | ✗ | if (!result) { | |
591 | ✗ | Unlock(); | |
592 | ✗ | return false; | |
593 | } | ||
594 | } | ||
595 | |||
596 | 2 | perf::Inc(statistics_.n_listing); | |
597 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | result = catalog->ListingPathStat(path, listing); |
598 | |||
599 | 2 | Unlock(); | |
600 | 2 | return result; | |
601 | } | ||
602 | |||
603 | |||
604 | /** | ||
605 | * Collect file chunks (if exist) | ||
606 | * @param path the path of the directory to list | ||
607 | * @param interpret_hashes_as hash of the directory entry (by convention the | ||
608 | * same than the chunk hashes) | ||
609 | * @return true if listing succeeded otherwise false | ||
610 | */ | ||
611 | template <class CatalogT> | ||
612 | ✗ | bool AbstractCatalogManager<CatalogT>::ListFileChunks( | |
613 | const PathString &path, | ||
614 | const shash::Algorithms interpret_hashes_as, | ||
615 | FileChunkList *chunks) | ||
616 | { | ||
617 | ✗ | EnforceSqliteMemLimit(); | |
618 | bool result; | ||
619 | ✗ | ReadLock(); | |
620 | |||
621 | // Find catalog, possibly load nested | ||
622 | ✗ | CatalogT *best_fit = FindCatalog(path); | |
623 | ✗ | CatalogT *catalog = best_fit; | |
624 | ✗ | if (MountSubtree(path, best_fit, false /* is_listable */, NULL)) { | |
625 | ✗ | StageNestedCatalogAndUnlock(path, best_fit, false); | |
626 | ✗ | WriteLock(); | |
627 | // Check again to avoid race | ||
628 | ✗ | best_fit = FindCatalog(path); | |
629 | ✗ | result = MountSubtree(path, best_fit, false /* is_listable */, &catalog); | |
630 | ✗ | if (!result) { | |
631 | ✗ | Unlock(); | |
632 | ✗ | return false; | |
633 | } | ||
634 | } | ||
635 | |||
636 | ✗ | result = catalog->ListPathChunks(path, interpret_hashes_as, chunks); | |
637 | |||
638 | ✗ | Unlock(); | |
639 | ✗ | return result; | |
640 | } | ||
641 | |||
642 | template <class CatalogT> | ||
643 | 3 | catalog::Counters AbstractCatalogManager<CatalogT>::LookupCounters( | |
644 | const PathString &path, | ||
645 | std::string *subcatalog_path, | ||
646 | shash::Any *hash | ||
647 | ) | ||
648 | { | ||
649 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | EnforceSqliteMemLimit(); |
650 | bool result; | ||
651 | 3 | ReadLock(); | |
652 | |||
653 | // Look past current path to mount up to intended location | ||
654 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | PathString catalog_path(path); |
655 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | catalog_path.Append("/.cvmfscatalog", 14); |
656 | |||
657 | // Find catalog, possibly load nested | ||
658 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | CatalogT *best_fit = FindCatalog(catalog_path); |
659 | 3 | CatalogT *catalog = best_fit; | |
660 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
|
3 | if (MountSubtree(catalog_path, best_fit, false /* is_listable */, NULL)) { |
661 | ✗ | StageNestedCatalogAndUnlock(path, best_fit, false /* is_listable */); | |
662 | ✗ | WriteLock(); | |
663 | // Check again to avoid race | ||
664 | ✗ | best_fit = FindCatalog(catalog_path); | |
665 | result = | ||
666 | ✗ | MountSubtree(catalog_path, best_fit, false /* is_listable */, &catalog); | |
667 | // Result is false if an available catalog failed to load (error happened) | ||
668 | ✗ | if (!result) { | |
669 | ✗ | Unlock(); | |
670 | ✗ | *subcatalog_path = "error: failed to load catalog!"; | |
671 | ✗ | *hash = shash::Any(); | |
672 | ✗ | return catalog::Counters(); | |
673 | } | ||
674 | } | ||
675 | |||
676 | 3 | *hash = catalog->hash(); | |
677 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | *subcatalog_path = catalog->mountpoint().ToString(); |
678 | 3 | catalog::Counters counters = catalog->GetCounters(); | |
679 | 3 | Unlock(); | |
680 | 3 | return counters; | |
681 | 3 | } | |
682 | |||
683 | |||
684 | template <class CatalogT> | ||
685 | 33 | uint64_t AbstractCatalogManager<CatalogT>::GetRevision() const { | |
686 | 33 | ReadLock(); | |
687 | 33 | const uint64_t revision = GetRevisionNoLock(); | |
688 | 33 | Unlock(); | |
689 | |||
690 | 33 | return revision; | |
691 | } | ||
692 | |||
693 | /** | ||
694 | * Like GetRevision() only without any locking mechanism. | ||
695 | * As such should only be used in conditions where a lock was already taken | ||
696 | * and calling GetRevision() would otherwise result in a deadlock. | ||
697 | */ | ||
698 | template <class CatalogT> | ||
699 | 59 | uint64_t AbstractCatalogManager<CatalogT>::GetRevisionNoLock() const { | |
700 | 59 | return revision_cache_; | |
701 | } | ||
702 | |||
703 | template <class CatalogT> | ||
704 | uint64_t AbstractCatalogManager<CatalogT>::GetTimestamp() const { | ||
705 | ReadLock(); | ||
706 | const uint64_t timestamp = GetTimestampNoLock(); | ||
707 | Unlock(); | ||
708 | |||
709 | return timestamp; | ||
710 | } | ||
711 | |||
712 | /** | ||
713 | * Like GetTimestamp() only without any locking mechanism. | ||
714 | * As such should only be used in conditions where a lock was already taken | ||
715 | * and calling GetTimestamp() would otherwise result in a deadlock. | ||
716 | */ | ||
717 | template <class CatalogT> | ||
718 | 6 | uint64_t AbstractCatalogManager<CatalogT>::GetTimestampNoLock() const { | |
719 | 6 | return timestamp_cache_; | |
720 | } | ||
721 | |||
722 | template <class CatalogT> | ||
723 | 25 | bool AbstractCatalogManager<CatalogT>::GetVOMSAuthz(std::string *authz) const { | |
724 | 25 | ReadLock(); | |
725 | 25 | const bool has_authz = has_authz_cache_; | |
726 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
25 | if (has_authz && authz) |
727 | ✗ | *authz = authz_cache_; | |
728 | 25 | Unlock(); | |
729 | 25 | return has_authz; | |
730 | } | ||
731 | |||
732 | |||
733 | template <class CatalogT> | ||
734 | ✗ | bool AbstractCatalogManager<CatalogT>::HasExplicitTTL() const { | |
735 | ✗ | ReadLock(); | |
736 | ✗ | const bool result = GetRootCatalog()->HasExplicitTTL(); | |
737 | ✗ | Unlock(); | |
738 | ✗ | return result; | |
739 | } | ||
740 | |||
741 | |||
742 | template <class CatalogT> | ||
743 | 1 | uint64_t AbstractCatalogManager<CatalogT>::GetTTL() const { | |
744 | 1 | ReadLock(); | |
745 | 1 | const uint64_t ttl = GetRootCatalog()->GetTTL(); | |
746 | 1 | Unlock(); | |
747 | 1 | return ttl; | |
748 | } | ||
749 | |||
750 | |||
751 | template <class CatalogT> | ||
752 | 33 | int AbstractCatalogManager<CatalogT>::GetNumCatalogs() const { | |
753 | 33 | ReadLock(); | |
754 | 33 | int result = catalogs_.size(); | |
755 | 33 | Unlock(); | |
756 | 33 | return result; | |
757 | } | ||
758 | |||
759 | |||
760 | /** | ||
761 | * Gets a formatted tree of the currently attached catalogs | ||
762 | */ | ||
763 | template <class CatalogT> | ||
764 | ✗ | string AbstractCatalogManager<CatalogT>::PrintHierarchy() const { | |
765 | ✗ | ReadLock(); | |
766 | ✗ | string output = PrintHierarchyRecursively(GetRootCatalog(), 0); | |
767 | ✗ | Unlock(); | |
768 | ✗ | return output; | |
769 | } | ||
770 | |||
771 | |||
772 | /** | ||
773 | * Assigns the next free numbers in the 64 bit space | ||
774 | */ | ||
775 | template <class CatalogT> | ||
776 | 130 | InodeRange AbstractCatalogManager<CatalogT>::AcquireInodes(uint64_t size) { | |
777 | 130 | InodeRange result; | |
778 | 130 | result.offset = inode_gauge_; | |
779 | 130 | result.size = size; | |
780 | |||
781 | 130 | inode_gauge_ += size; | |
782 | 130 | LogCvmfs(kLogCatalog, kLogDebug, "allocating inodes from %lu to %lu.", | |
783 |
1/2✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
|
130 | result.offset + 1, inode_gauge_); |
784 | |||
785 | 130 | return result; | |
786 | } | ||
787 | |||
788 | |||
789 | /** | ||
790 | * Called if a catalog is detached which renders the associated InodeChunk | ||
791 | * invalid. | ||
792 | * @param chunk the InodeChunk to be freed | ||
793 | */ | ||
794 | template <class CatalogT> | ||
795 | 130 | void AbstractCatalogManager<CatalogT>::ReleaseInodes(const InodeRange chunk) { | |
796 | // TODO(jblomer) currently inodes are only released on remount | ||
797 | 130 | } | |
798 | |||
799 | |||
800 | /** | ||
801 | * Find the catalog leaf in the tree that fits the path. | ||
802 | * The path might be served by a not yet loaded nested catalog. | ||
803 | * @param path the path a catalog is searched for | ||
804 | * @return the catalog which is best fitting at the given path | ||
805 | */ | ||
806 | template <class CatalogT> | ||
807 | 487 | CatalogT* AbstractCatalogManager<CatalogT>::FindCatalog( | |
808 | const PathString &path) const { | ||
809 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 487 times.
|
487 | assert(catalogs_.size() > 0); |
810 | |||
811 | // Start at the root catalog and successively go down the catalog tree | ||
812 | 487 | CatalogT *best_fit = GetRootCatalog(); | |
813 | 487 | CatalogT *next_fit = NULL; | |
814 |
5/5✓ Branch 2 taken 71 times.
✓ Branch 3 taken 436 times.
✓ Branch 4 taken 64 times.
✓ Branch 5 taken 63 times.
✓ Branch 6 taken 8 times.
|
571 | while (best_fit->mountpoint() != path) { |
815 | 499 | next_fit = best_fit->FindSubtree(path); | |
816 |
2/2✓ Branch 0 taken 415 times.
✓ Branch 1 taken 84 times.
|
499 | if (next_fit == NULL) |
817 | 415 | break; | |
818 | 84 | best_fit = next_fit; | |
819 | } | ||
820 | |||
821 | 487 | return best_fit; | |
822 | } | ||
823 | |||
824 | |||
825 | /** | ||
826 | * Checks if a searched catalog is already mounted to this CatalogManager | ||
827 | * @param root_path the root path of the searched catalog | ||
828 | * @param attached_catalog is set to the searched catalog, if not NULL | ||
829 | * @return true if catalog is already present, false otherwise | ||
830 | */ | ||
831 | template <class CatalogT> | ||
832 | 108 | bool AbstractCatalogManager<CatalogT>::IsAttached(const PathString &root_path, | |
833 | CatalogT **attached_catalog) const | ||
834 | { | ||
835 |
2/2✓ Branch 1 taken 72 times.
✓ Branch 2 taken 36 times.
|
108 | if (catalogs_.size() == 0) |
836 | 72 | return false; | |
837 | |||
838 | 36 | CatalogT *best_fit = FindCatalog(root_path); | |
839 |
4/5✓ Branch 2 taken 21 times.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 17 times.
✓ Branch 6 taken 4 times.
|
36 | if (best_fit->mountpoint() != root_path) |
840 | 32 | return false; | |
841 | |||
842 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (attached_catalog != NULL) *attached_catalog = best_fit; |
843 | 4 | return true; | |
844 | } | ||
845 | |||
846 | |||
847 | template <class CatalogT> | ||
848 | 24 | void AbstractCatalogManager<CatalogT>::StageNestedCatalogAndUnlock( | |
849 | const PathString &path, | ||
850 | const CatalogT *parent, | ||
851 | bool is_listable) | ||
852 | { | ||
853 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | assert(parent); |
854 | 24 | const unsigned path_len = path.GetLength(); | |
855 | |||
856 | 24 | perf::Inc(statistics_.n_nested_listing); | |
857 | typedef typename CatalogT::NestedCatalogList NestedCatalogList; | ||
858 | 24 | const NestedCatalogList& nested_catalogs = parent->ListNestedCatalogs(); | |
859 | |||
860 | 48 | for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(), | |
861 |
1/2✓ Branch 3 taken 29 times.
✗ Branch 4 not taken.
|
53 | iEnd = nested_catalogs.end(); i != iEnd; ++i) |
862 | { | ||
863 |
2/2✓ Branch 2 taken 5 times.
✓ Branch 3 taken 24 times.
|
29 | if (!path.StartsWith(i->mountpoint)) |
864 | 5 | continue; | |
865 | |||
866 | // in this case the path doesn't start with | ||
867 | // the mountpoint in a file path sense | ||
868 | // (e.g. path is /a/bc and mountpoint is /a/b), and will be ignored | ||
869 | 24 | const unsigned mountpoint_len = i->mountpoint.GetLength(); | |
870 |
4/6✓ Branch 0 taken 17 times.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
|
24 | if (path_len > mountpoint_len && path.GetChars()[mountpoint_len] != '/') |
871 | ✗ | continue; | |
872 | |||
873 | // Found a nested catalog transition point | ||
874 |
4/4✓ Branch 0 taken 14 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
|
24 | if (!is_listable && (path_len == mountpoint_len)) |
875 | 2 | break; | |
876 | |||
877 | 22 | Unlock(); | |
878 |
2/4✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 22 times.
✗ Branch 7 not taken.
|
44 | LogCvmfs(kLogCatalog, kLogDebug, "staging nested catalog at %s (%s)", |
879 | 44 | i->mountpoint.c_str(), i->hash.ToString().c_str()); | |
880 |
1/2✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
|
22 | StageNestedCatalogByHash(i->hash, i->mountpoint); |
881 | 22 | return; | |
882 | } | ||
883 | 2 | Unlock(); | |
884 | } | ||
885 | |||
886 | /** | ||
887 | * Recursively mounts all nested catalogs required to serve a path. | ||
888 | * If leaf_catalog is NULL, just indicate if it is necessary to load a | ||
889 | * nested catalog for the given path. | ||
890 | * The final leaf nested catalog is returned. | ||
891 | * The is_listable parameter is relevant if path is a nested catalog. Only | ||
892 | * if is_listable is true, the nested catalog will be used; otherwise the parent | ||
893 | * with the transaction point is sufficient. | ||
894 | */ | ||
895 | template <class CatalogT> | ||
896 | 440 | bool AbstractCatalogManager<CatalogT>::MountSubtree( | |
897 | const PathString &path, | ||
898 | const CatalogT *entry_point, | ||
899 | bool is_listable, | ||
900 | CatalogT **leaf_catalog) | ||
901 | { | ||
902 | 440 | bool result = true; | |
903 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 440 times.
|
440 | CatalogT *parent = (entry_point == NULL) ? |
904 | ✗ | GetRootCatalog() : const_cast<CatalogT *>(entry_point); | |
905 |
2/5✓ Branch 1 taken 440 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 440 times.
|
440 | assert(path.StartsWith(parent->mountpoint())); |
906 | |||
907 | 440 | unsigned path_len = path.GetLength(); | |
908 | |||
909 | // Try to find path as a super string of nested catalog mount points | ||
910 | 440 | perf::Inc(statistics_.n_nested_listing); | |
911 | typedef typename CatalogT::NestedCatalogList NestedCatalogList; | ||
912 | const NestedCatalogList& nested_catalogs = | ||
913 |
1/2✓ Branch 1 taken 384 times.
✗ Branch 2 not taken.
|
440 | parent->ListNestedCatalogs(); |
914 | 880 | for (typename NestedCatalogList::const_iterator i = nested_catalogs.begin(), | |
915 |
2/2✓ Branch 3 taken 155 times.
✓ Branch 4 taken 378 times.
|
973 | iEnd = nested_catalogs.end(); i != iEnd; ++i) |
916 | { | ||
917 | // Next nesting level | ||
918 |
2/2✓ Branch 2 taken 65 times.
✓ Branch 3 taken 90 times.
|
155 | if (path.StartsWith(i->mountpoint)) { |
919 | // in this case the path doesn't start with | ||
920 | // the mountpoint in a file path sense | ||
921 | // (e.g. path is /a/bc and mountpoint is /a/b), and will be ignored | ||
922 | 65 | unsigned mountpoint_len = i->mountpoint.GetLength(); | |
923 |
6/6✓ Branch 0 taken 46 times.
✓ Branch 1 taken 19 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 43 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 62 times.
|
65 | if (path_len > mountpoint_len && path.GetChars()[mountpoint_len] != '/') |
924 | 3 | continue; | |
925 | |||
926 | // Found a nested catalog transition point | ||
927 |
4/4✓ Branch 0 taken 34 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 32 times.
|
62 | if (!is_listable && (path_len == mountpoint_len)) |
928 | 2 | break; | |
929 | |||
930 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 36 times.
|
60 | if (leaf_catalog == NULL) |
931 | 24 | return true; | |
932 | CatalogT *new_nested; | ||
933 |
1/2✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
|
36 | LogCvmfs(kLogCatalog, kLogDebug, "load nested catalog at %s", |
934 | 36 | i->mountpoint.c_str()); | |
935 | // prevent endless recursion with corrupted catalogs | ||
936 | // (due to reloading root) | ||
937 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
|
36 | if (i->hash.IsNull()) |
938 | ✗ | return false; | |
939 |
1/2✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
|
36 | new_nested = MountCatalog(i->mountpoint, i->hash, parent); |
940 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (!new_nested) |
941 | ✗ | return false; | |
942 | |||
943 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | result = MountSubtree(path, new_nested, is_listable, &parent); |
944 | 36 | break; | |
945 | } | ||
946 | } | ||
947 | |||
948 |
2/2✓ Branch 0 taken 57 times.
✓ Branch 1 taken 359 times.
|
416 | if (leaf_catalog == NULL) |
949 | 57 | return false; | |
950 | 359 | *leaf_catalog = parent; | |
951 | 359 | return result; | |
952 | } | ||
953 | |||
954 | |||
955 | /** | ||
956 | * Load a catalog file and attach it to the tree of Catalog objects. | ||
957 | * Loading of catalogs is implemented by derived classes. | ||
958 | */ | ||
959 | template <class CatalogT> | ||
960 | 108 | CatalogT *AbstractCatalogManager<CatalogT>::MountCatalog( | |
961 | const PathString &mountpoint, | ||
962 | const shash::Any &hash, | ||
963 | CatalogT *parent_catalog) | ||
964 | { | ||
965 | 108 | CatalogT *attached_catalog = NULL; | |
966 |
3/4✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 104 times.
|
108 | if (IsAttached(mountpoint, &attached_catalog)) { |
967 | 4 | return attached_catalog; | |
968 | } | ||
969 | |||
970 |
1/2✓ Branch 1 taken 104 times.
✗ Branch 2 not taken.
|
104 | CatalogContext ctlg_context(hash, mountpoint, kCtlgLocationMounted); |
971 | |||
972 |
7/9✓ Branch 1 taken 104 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 57 times.
✓ Branch 7 taken 15 times.
✓ Branch 8 taken 57 times.
✓ Branch 9 taken 47 times.
|
104 | if (ctlg_context.IsRootCatalog() && hash.IsNull()) { |
973 |
3/4✓ Branch 1 taken 57 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 52 times.
|
57 | if (GetNewRootCatalogContext(&ctlg_context) == kLoadFail) { |
974 |
1/2✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | LogCvmfs(kLogCatalog, kLogDebug, |
975 | "failed to retrieve valid root catalog '%s'", | ||
976 | mountpoint.c_str()); | ||
977 | 5 | return NULL; | |
978 | } | ||
979 | } | ||
980 | |||
981 |
1/2✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
|
99 | const LoadReturn retval = LoadCatalogByHash(&ctlg_context); |
982 |
2/4✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 99 times.
|
99 | if ((retval == kLoadFail) || (retval == kLoadNoSpace)) { |
983 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, "failed to load catalog '%s' (%d - %s)", | |
984 | mountpoint.c_str(), retval, Code2Ascii(retval)); | ||
985 | ✗ | return NULL; | |
986 | } | ||
987 | |||
988 |
2/4✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
|
99 | attached_catalog = CreateCatalog(ctlg_context.mountpoint(), |
989 | 99 | ctlg_context.hash(), | |
990 | parent_catalog); | ||
991 | |||
992 | // Attach loaded catalog | ||
993 |
3/7✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 99 times.
|
99 | if (!AttachCatalog(ctlg_context.sqlite_path(), attached_catalog)) { |
994 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, "failed to attach catalog '%s'", | |
995 | mountpoint.c_str()); | ||
996 | ✗ | UnloadCatalog(attached_catalog); | |
997 | ✗ | return NULL; | |
998 | } | ||
999 | |||
1000 |
6/6✓ Branch 0 taken 7 times.
✓ Branch 1 taken 92 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 96 times.
|
99 | if ((catalog_watermark_ > 0) && (catalogs_.size() >= catalog_watermark_)) { |
1001 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | DetachSiblings(mountpoint); |
1002 | } | ||
1003 | |||
1004 | 99 | return attached_catalog; | |
1005 | 104 | } | |
1006 | |||
1007 | |||
1008 | /** | ||
1009 | * Load a catalog file as a freestanding Catalog object. | ||
1010 | * Loading of catalogs is implemented by derived classes. | ||
1011 | */ | ||
1012 | template <class CatalogT> | ||
1013 | 8 | CatalogT *AbstractCatalogManager<CatalogT>::LoadFreeCatalog( | |
1014 | const PathString &mountpoint, | ||
1015 | const shash::Any &hash) | ||
1016 | { | ||
1017 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
|
8 | assert(!hash.IsNull()); |
1018 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | CatalogContext ctlg_context(hash, mountpoint, kCtlgNoLocationNeeded); |
1019 | |||
1020 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
|
8 | const LoadReturn load_ret = LoadCatalogByHash(&ctlg_context); |
1021 | |||
1022 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (load_ret != kLoadNew) { |
1023 | ✗ | return NULL; | |
1024 | } | ||
1025 | |||
1026 |
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.
|
7 | CatalogT *catalog = CatalogT::AttachFreely(mountpoint.ToString(), |
1027 | ctlg_context.sqlite_path(), | ||
1028 | 7 | ctlg_context.hash()); | |
1029 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | catalog->TakeDatabaseFileOwnership(); |
1030 | 7 | return catalog; | |
1031 | 8 | } | |
1032 | |||
1033 | |||
1034 | /** | ||
1035 | * Attaches a newly created catalog. | ||
1036 | * @param db_path the file on a local file system containing the database | ||
1037 | * @param new_catalog the catalog to attach to this CatalogManager | ||
1038 | * @return true on success, false otherwise | ||
1039 | */ | ||
1040 | template <class CatalogT> | ||
1041 | 130 | bool AbstractCatalogManager<CatalogT>::AttachCatalog(const string &db_path, | |
1042 | CatalogT *new_catalog) | ||
1043 | { | ||
1044 |
1/2✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
|
130 | LogCvmfs(kLogCatalog, kLogDebug, "attaching catalog file %s", |
1045 | db_path.c_str()); | ||
1046 | |||
1047 | // Initialize the new catalog | ||
1048 |
2/4✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 130 times.
|
130 | if (!new_catalog->OpenDatabase(db_path)) { |
1049 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, "initialization of catalog %s failed", | |
1050 | db_path.c_str()); | ||
1051 | ✗ | return false; | |
1052 | } | ||
1053 | |||
1054 | // Determine the inode offset of this catalog | ||
1055 | 130 | uint64_t inode_chunk_size = new_catalog->max_row_id(); | |
1056 |
1/2✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
|
130 | InodeRange range = AcquireInodes(inode_chunk_size); |
1057 | 130 | new_catalog->set_inode_range(range); | |
1058 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
130 | new_catalog->SetInodeAnnotation(inode_annotation_); |
1059 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
130 | new_catalog->SetOwnerMaps(&uid_map_, &gid_map_); |
1060 | |||
1061 | // Add catalog to the manager | ||
1062 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 130 times.
|
130 | if (!new_catalog->IsInitialized()) { |
1063 | ✗ | LogCvmfs(kLogCatalog, kLogDebug, | |
1064 | "catalog initialization failed (obscure data)"); | ||
1065 | ✗ | inode_gauge_ -= inode_chunk_size; | |
1066 | ✗ | return false; | |
1067 | } | ||
1068 |
1/2✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
|
130 | CheckInodeWatermark(); |
1069 | |||
1070 | // The revision of the catalog tree is given by the root catalog revision | ||
1071 |
2/2✓ Branch 1 taken 69 times.
✓ Branch 2 taken 61 times.
|
130 | if (catalogs_.empty()) { |
1072 |
1/2✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
|
69 | revision_cache_ = new_catalog->GetRevision(); |
1073 |
1/2✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
|
69 | timestamp_cache_ = new_catalog->GetLastModified(); |
1074 | 69 | statistics_.catalog_revision->Set(revision_cache_); | |
1075 |
1/2✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
|
69 | has_authz_cache_ = new_catalog->GetVOMSAuthz(&authz_cache_); |
1076 | 69 | volatile_flag_ = new_catalog->volatile_flag(); | |
1077 | } | ||
1078 | |||
1079 |
1/2✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
|
130 | catalogs_.push_back(new_catalog); |
1080 |
1/2✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
|
130 | ActivateCatalog(new_catalog); |
1081 | 130 | return true; | |
1082 | } | ||
1083 | |||
1084 | |||
1085 | /** | ||
1086 | * Removes a catalog from this CatalogManager, the catalog pointer is | ||
1087 | * freed if the call succeeds. | ||
1088 | * This method can create dangling children if a catalog in the middle of | ||
1089 | * a tree is removed. | ||
1090 | * @param catalog the catalog to detach | ||
1091 | * @return true on success, false otherwise | ||
1092 | */ | ||
1093 | template <class CatalogT> | ||
1094 | 130 | void AbstractCatalogManager<CatalogT>::DetachCatalog(CatalogT *catalog) { | |
1095 |
2/2✓ Branch 1 taken 60 times.
✓ Branch 2 taken 70 times.
|
130 | if (catalog->HasParent()) |
1096 |
1/2✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
|
60 | catalog->parent()->RemoveChild(catalog); |
1097 | |||
1098 | 130 | ReleaseInodes(catalog->inode_range()); | |
1099 |
1/2✓ Branch 1 taken 130 times.
✗ Branch 2 not taken.
|
130 | UnloadCatalog(catalog); |
1100 | |||
1101 | // Delete catalog from internal lists | ||
1102 | 130 | typename CatalogList::iterator i; | |
1103 | 130 | typename CatalogList::const_iterator iend; | |
1104 |
1/2✓ Branch 5 taken 222 times.
✗ Branch 6 not taken.
|
222 | for (i = catalogs_.begin(), iend = catalogs_.end(); i != iend; ++i) { |
1105 |
2/2✓ Branch 1 taken 130 times.
✓ Branch 2 taken 92 times.
|
222 | if (*i == catalog) { |
1106 |
1/2✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
|
130 | catalogs_.erase(i); |
1107 |
1/2✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
|
130 | delete catalog; |
1108 | 130 | return; | |
1109 | } | ||
1110 | } | ||
1111 | |||
1112 | ✗ | assert(false); | |
1113 | } | ||
1114 | |||
1115 | |||
1116 | /** | ||
1117 | * Removes a catalog (and all of it's children) from this CatalogManager. | ||
1118 | * The given catalog and all children are freed, if this call succeeds. | ||
1119 | * @param catalog the catalog to detach | ||
1120 | * @return true on success, false otherwise | ||
1121 | */ | ||
1122 | template <class CatalogT> | ||
1123 | 129 | void AbstractCatalogManager<CatalogT>::DetachSubtree(CatalogT *catalog) { | |
1124 | // Detach all child catalogs recursively | ||
1125 | 129 | typename CatalogList::const_iterator i; | |
1126 | 129 | typename CatalogList::const_iterator iend; | |
1127 |
1/2✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
|
129 | CatalogList catalogs_to_detach = catalog->GetChildren(); |
1128 | 129 | for (i = catalogs_to_detach.begin(), iend = catalogs_to_detach.end(); | |
1129 |
2/2✓ Branch 2 taken 55 times.
✓ Branch 3 taken 129 times.
|
184 | i != iend; ++i) |
1130 | { | ||
1131 |
1/2✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
|
55 | DetachSubtree(*i); |
1132 | } | ||
1133 | |||
1134 |
1/2✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
|
129 | DetachCatalog(catalog); |
1135 | 129 | } | |
1136 | |||
1137 | |||
1138 | /** | ||
1139 | * Detaches all nested catalogs that are not on a prefix of the given tree. | ||
1140 | * Used when the catalog_watermark_ is surpassed. | ||
1141 | */ | ||
1142 | template <class CatalogT> | ||
1143 | 4 | void AbstractCatalogManager<CatalogT>::DetachSiblings( | |
1144 | const PathString ¤t_tree) | ||
1145 | { | ||
1146 | bool again; | ||
1147 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | do { |
1148 | 4 | again = false; | |
1149 | 4 | unsigned N = catalogs_.size(); | |
1150 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
|
12 | for (unsigned i = 0; i < N; ++i) { |
1151 |
5/9✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 9 times.
✗ Branch 9 not taken.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 8 times.
|
9 | if (!HasPrefix(current_tree.ToString(), |
1152 | 9 | catalogs_[i]->mountpoint().ToString(), | |
1153 | false /* ignore_case */)) | ||
1154 | { | ||
1155 | 1 | DetachSubtree(catalogs_[i]); | |
1156 | 1 | again = true; | |
1157 | 1 | break; | |
1158 | } | ||
1159 | } | ||
1160 | } while (again); | ||
1161 | 3 | perf::Inc(statistics_.n_detach_siblings); | |
1162 | 3 | } | |
1163 | |||
1164 | |||
1165 | /** | ||
1166 | * Formats the catalog hierarchy | ||
1167 | */ | ||
1168 | template <class CatalogT> | ||
1169 | ✗ | string AbstractCatalogManager<CatalogT>::PrintHierarchyRecursively( | |
1170 | const CatalogT *catalog, | ||
1171 | const int level) const | ||
1172 | { | ||
1173 | ✗ | string output; | |
1174 | |||
1175 | // Indent according to level | ||
1176 | ✗ | for (int i = 0; i < level; ++i) | |
1177 | ✗ | output += " "; | |
1178 | |||
1179 | ✗ | output += "-> " + string(catalog->mountpoint().GetChars(), | |
1180 | ✗ | catalog->mountpoint().GetLength()) | |
1181 | + "\n"; | ||
1182 | |||
1183 | ✗ | CatalogList children = catalog->GetChildren(); | |
1184 | ✗ | typename CatalogList::const_iterator i = children.begin(); | |
1185 | ✗ | typename CatalogList::const_iterator iend = children.end(); | |
1186 | ✗ | for (; i != iend; ++i) { | |
1187 | ✗ | output += PrintHierarchyRecursively(*i, level + 1); | |
1188 | } | ||
1189 | |||
1190 | ✗ | return output; | |
1191 | } | ||
1192 | |||
1193 | |||
1194 | template <class CatalogT> | ||
1195 | ✗ | std::string AbstractCatalogManager<CatalogT>::PrintMemStatsRecursively( | |
1196 | const CatalogT *catalog) const | ||
1197 | { | ||
1198 | ✗ | string result = catalog->PrintMemStatistics() + "\n"; | |
1199 | |||
1200 | ✗ | CatalogList children = catalog->GetChildren(); | |
1201 | ✗ | typename CatalogList::const_iterator i = children.begin(); | |
1202 | ✗ | typename CatalogList::const_iterator iend = children.end(); | |
1203 | ✗ | for (; i != iend; ++i) { | |
1204 | ✗ | result += PrintMemStatsRecursively(*i); | |
1205 | } | ||
1206 | ✗ | return result; | |
1207 | } | ||
1208 | |||
1209 | |||
1210 | /** | ||
1211 | * Statistics from all catalogs | ||
1212 | */ | ||
1213 | template <class CatalogT> | ||
1214 | ✗ | std::string AbstractCatalogManager<CatalogT>::PrintAllMemStatistics() const { | |
1215 | ✗ | string result; | |
1216 | ✗ | ReadLock(); | |
1217 | ✗ | result = PrintMemStatsRecursively(GetRootCatalog()); | |
1218 | ✗ | Unlock(); | |
1219 | ✗ | return result; | |
1220 | } | ||
1221 | |||
1222 | |||
1223 | template <class CatalogT> | ||
1224 | 70 | void AbstractCatalogManager<CatalogT>::EnforceSqliteMemLimit() { | |
1225 | char *mem_enforced = | ||
1226 | 70 | static_cast<char *>(pthread_getspecific(pkey_sqlitemem_)); | |
1227 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 53 times.
|
70 | if (mem_enforced == NULL) { |
1228 | 17 | sqlite3_soft_heap_limit(kSqliteMemPerThread); | |
1229 | 17 | pthread_setspecific(pkey_sqlitemem_, this); | |
1230 | } | ||
1231 | 70 | } | |
1232 | |||
1233 | } // namespace catalog | ||
1234 | |||
1235 | |||
1236 | |||
1237 | |||
1238 | #endif // CVMFS_CATALOG_MGR_IMPL_H_ | ||
1239 | //NOLINTEND | ||
1240 |