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