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