Directory: | cvmfs/ |
---|---|
File: | cvmfs/cache_posix.cc |
Date: | 2025-06-29 02:35:41 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 284 | 322 | 88.2% |
Branches: | 168 | 267 | 62.9% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | * | ||
4 | * The cache module maintains the local file cache. Files are | ||
5 | * staged into the cache by Fetch(). The cache stores files with a name | ||
6 | * according to their content hash. | ||
7 | * | ||
8 | * The procedure is | ||
9 | * -# Look in the catalog for content hash | ||
10 | * -# If it is in local cache: return file descriptor | ||
11 | * -# Otherwise download, store in cache and return fd | ||
12 | * | ||
13 | * Each running CVMFS instance has to have a separate cache directory. | ||
14 | * The local cache directory (directories 00..ff) can be accessed | ||
15 | * in parallel to a running CVMFS, i.e. files can be deleted for instance | ||
16 | * anytime. However, this will confuse the cache database managed by the lru | ||
17 | * module. | ||
18 | * | ||
19 | * Files are created in txn directory first. At the very latest | ||
20 | * point they are renamed into their "real" content hash names atomically by | ||
21 | * rename(). This concept is taken over from GROW-FS. | ||
22 | * | ||
23 | * Identical URLs won't be concurrently downloaded. The first thread performs | ||
24 | * the download and informs the other, waiting threads on pipes. | ||
25 | */ | ||
26 | |||
27 | #define __STDC_FORMAT_MACROS | ||
28 | |||
29 | |||
30 | #include "cache_posix.h" | ||
31 | |||
32 | #include <dirent.h> | ||
33 | #include <errno.h> | ||
34 | #include <fcntl.h> | ||
35 | #include <inttypes.h> | ||
36 | #include <pthread.h> | ||
37 | #include <sys/stat.h> | ||
38 | #include <sys/types.h> | ||
39 | #ifndef __APPLE__ | ||
40 | #include <sys/statfs.h> | ||
41 | #endif | ||
42 | #include <unistd.h> | ||
43 | |||
44 | #include <algorithm> | ||
45 | #include <cassert> | ||
46 | #include <cstdio> | ||
47 | #include <cstdlib> | ||
48 | #include <cstring> | ||
49 | #include <map> | ||
50 | #include <vector> | ||
51 | |||
52 | #include "crypto/hash.h" | ||
53 | #include "crypto/signature.h" | ||
54 | #include "directory_entry.h" | ||
55 | #include "manifest.h" | ||
56 | #include "manifest_fetch.h" | ||
57 | #include "network/download.h" | ||
58 | #include "quota.h" | ||
59 | #include "shortstring.h" | ||
60 | #include "statistics.h" | ||
61 | #include "util/atomic.h" | ||
62 | #include "util/logging.h" | ||
63 | #include "util/platform.h" | ||
64 | #include "util/posix.h" | ||
65 | #include "util/smalloc.h" | ||
66 | |||
67 | using namespace std; // NOLINT | ||
68 | |||
69 | namespace { | ||
70 | |||
71 | /** | ||
72 | * A CallGuard object can be placed at the beginning of a function. It counts | ||
73 | * the number of so-annotated functions that are in flight. The Drainout() call | ||
74 | * will wait until all functions that have been called so far are finished. | ||
75 | * | ||
76 | * The class is used in order to wait for remaining calls when switching into | ||
77 | * the read-only cache mode. | ||
78 | */ | ||
79 | class CallGuard { | ||
80 | public: | ||
81 | CallGuard() { | ||
82 | const int32_t global_drainout = atomic_read32(&global_drainout_); | ||
83 | drainout_ = (global_drainout != 0); | ||
84 | if (!drainout_) | ||
85 | atomic_inc32(&num_inflight_calls_); | ||
86 | } | ||
87 | ~CallGuard() { | ||
88 | if (!drainout_) | ||
89 | atomic_dec32(&num_inflight_calls_); | ||
90 | } | ||
91 | static void Drainout() { | ||
92 | atomic_cas32(&global_drainout_, 0, 1); | ||
93 | while (atomic_read32(&num_inflight_calls_) != 0) | ||
94 | SafeSleepMs(50); | ||
95 | } | ||
96 | |||
97 | private: | ||
98 | bool drainout_; | ||
99 | static atomic_int32 global_drainout_; | ||
100 | static atomic_int32 num_inflight_calls_; | ||
101 | }; | ||
102 | atomic_int32 CallGuard::num_inflight_calls_ = 0; | ||
103 | atomic_int32 CallGuard::global_drainout_ = 0; | ||
104 | |||
105 | } // anonymous namespace | ||
106 | |||
107 | |||
108 | //------------------------------------------------------------------------------ | ||
109 | |||
110 | |||
111 | const uint64_t PosixCacheManager::kBigFile = 25 * 1024 * 1024; // 25M | ||
112 | |||
113 | |||
114 | 685 | int PosixCacheManager::AbortTxn(void *txn) { | |
115 | 685 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
116 | 685 | LogCvmfs(kLogCache, kLogDebug, "abort %s", transaction->tmp_path.c_str()); | |
117 | 685 | close(transaction->fd); | |
118 | 685 | const int result = unlink(transaction->tmp_path.c_str()); | |
119 | 685 | transaction->~Transaction(); | |
120 | 685 | atomic_dec32(&no_inflight_txns_); | |
121 |
2/2✓ Branch 0 taken 95 times.
✓ Branch 1 taken 590 times.
|
685 | if (result == -1) |
122 | 95 | return -errno; | |
123 | 590 | return 0; | |
124 | } | ||
125 | |||
126 | |||
127 | /** | ||
128 | * This should only be used to replace the default NoopQuotaManager by a | ||
129 | * PosixQuotaManager. The cache manager takes the ownership of the passed | ||
130 | * quota manager. | ||
131 | */ | ||
132 | 721 | bool PosixCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) { | |
133 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 721 times.
|
721 | if (quota_mgr == NULL) |
134 | ✗ | return false; | |
135 |
1/2✓ Branch 0 taken 721 times.
✗ Branch 1 not taken.
|
721 | delete quota_mgr_; |
136 | 721 | quota_mgr_ = quota_mgr; | |
137 | 721 | return true; | |
138 | } | ||
139 | |||
140 | |||
141 | 2041 | int PosixCacheManager::Close(int fd) { | |
142 |
1/2✓ Branch 0 taken 2041 times.
✗ Branch 1 not taken.
|
2041 | const int retval = do_refcount_ ? fd_mgr_->Close(fd) : close(fd); |
143 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1992 times.
|
2041 | if (retval != 0) |
144 | 49 | return -errno; | |
145 | 1992 | return 0; | |
146 | } | ||
147 | |||
148 | |||
149 | 5254 | int PosixCacheManager::CommitTxn(void *txn) { | |
150 | 5254 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
151 | int result; | ||
152 | 5254 | LogCvmfs(kLogCache, kLogDebug, "commit %s %s", | |
153 | transaction->final_path.c_str(), transaction->tmp_path.c_str()); | ||
154 | |||
155 | 5254 | result = Flush(transaction); | |
156 | 5254 | close(transaction->fd); | |
157 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 5205 times.
|
5254 | if (result < 0) { |
158 | 49 | unlink(transaction->tmp_path.c_str()); | |
159 | 49 | transaction->~Transaction(); | |
160 | 49 | atomic_dec32(&no_inflight_txns_); | |
161 | 49 | return result; | |
162 | } | ||
163 | |||
164 | // To support debugging, move files into quarantine on file size mismatch | ||
165 |
2/2✓ Branch 0 taken 652 times.
✓ Branch 1 taken 4553 times.
|
5205 | if (transaction->size != transaction->expected_size) { |
166 | // Allow size to be zero if alien cache, because hadoop-fuse-dfs returns | ||
167 | // size zero for a while | ||
168 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 601 times.
|
652 | if ((transaction->expected_size != kSizeUnknown) |
169 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
51 | && (reports_correct_filesize_ || (transaction->size != 0))) { |
170 |
1/3✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
51 | LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr, |
171 | "size check failure for %s, expected %lu, got %lu", | ||
172 | 102 | transaction->id.ToString().c_str(), transaction->expected_size, | |
173 | transaction->size); | ||
174 |
1/2✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
|
51 | CopyPath2Path(transaction->tmp_path, |
175 |
2/4✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 51 times.
✗ Branch 6 not taken.
|
102 | cache_path_ + "/quarantaine/" + transaction->id.ToString()); |
176 | 51 | unlink(transaction->tmp_path.c_str()); | |
177 | 51 | transaction->~Transaction(); | |
178 | 51 | atomic_dec32(&no_inflight_txns_); | |
179 | 51 | return -EIO; | |
180 | } | ||
181 | } | ||
182 | |||
183 |
2/2✓ Branch 0 taken 5105 times.
✓ Branch 1 taken 49 times.
|
5154 | if ((transaction->label.flags & kLabelPinned) |
184 |
2/2✓ Branch 0 taken 724 times.
✓ Branch 1 taken 4381 times.
|
5105 | || (transaction->label.flags & kLabelCatalog)) { |
185 | 2319 | const bool retval = quota_mgr_->Pin( | |
186 |
1/2✓ Branch 1 taken 773 times.
✗ Branch 2 not taken.
|
773 | transaction->id, transaction->size, transaction->label.GetDescription(), |
187 | 773 | (transaction->label.flags & kLabelCatalog)); | |
188 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 724 times.
|
773 | if (!retval) { |
189 |
1/2✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
|
49 | LogCvmfs(kLogCache, kLogDebug, "commit failed: cannot pin %s", |
190 | 98 | transaction->id.ToString().c_str()); | |
191 | 49 | unlink(transaction->tmp_path.c_str()); | |
192 | 49 | transaction->~Transaction(); | |
193 | 49 | atomic_dec32(&no_inflight_txns_); | |
194 | 49 | return -ENOSPC; | |
195 | } | ||
196 | } | ||
197 | |||
198 | // Move the temporary file into its final location | ||
199 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 5056 times.
|
5105 | if (alien_cache_) { |
200 | 49 | const int retval = chmod(transaction->tmp_path.c_str(), 0660); | |
201 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
|
49 | assert(retval == 0); |
202 | } | ||
203 | 5105 | result = Rename(transaction->tmp_path.c_str(), | |
204 | transaction->final_path.c_str()); | ||
205 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 5007 times.
|
5105 | if (result < 0) { |
206 | 98 | LogCvmfs(kLogCache, kLogDebug, "commit failed: %s", strerror(errno)); | |
207 | 98 | unlink(transaction->tmp_path.c_str()); | |
208 |
1/2✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
|
98 | if ((transaction->label.flags & kLabelPinned) |
209 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 49 times.
|
98 | || (transaction->label.flags & kLabelCatalog)) { |
210 | 49 | quota_mgr_->Remove(transaction->id); | |
211 | } | ||
212 | } else { | ||
213 | // Success, inform quota manager | ||
214 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 4958 times.
|
5007 | if (transaction->label.flags & kLabelVolatile) { |
215 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
49 | quota_mgr_->InsertVolatile(transaction->id, transaction->size, |
216 | 98 | transaction->label.GetDescription()); | |
217 | 4958 | } else if (!transaction->label.IsCatalog() | |
218 |
6/6✓ Branch 0 taken 4332 times.
✓ Branch 1 taken 626 times.
✓ Branch 3 taken 4283 times.
✓ Branch 4 taken 49 times.
✓ Branch 5 taken 4283 times.
✓ Branch 6 taken 675 times.
|
4958 | && !transaction->label.IsPinned()) { |
219 |
1/2✓ Branch 1 taken 4283 times.
✗ Branch 2 not taken.
|
4283 | quota_mgr_->Insert(transaction->id, transaction->size, |
220 | 8566 | transaction->label.GetDescription()); | |
221 | } | ||
222 | } | ||
223 | 5105 | transaction->~Transaction(); | |
224 | 5105 | atomic_dec32(&no_inflight_txns_); | |
225 | 5105 | return result; | |
226 | } | ||
227 | |||
228 | 6098 | bool PosixCacheManager::InitCacheDirectory(const string &cache_path) { | |
229 |
1/2✓ Branch 1 taken 6098 times.
✗ Branch 2 not taken.
|
6098 | const FileSystemInfo fs_info = GetFileSystemInfo(cache_path); |
230 | |||
231 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 6000 times.
|
6098 | if (fs_info.type == kFsTypeTmpfs) { |
232 | 98 | is_tmpfs_ = true; | |
233 | } | ||
234 | |||
235 |
2/2✓ Branch 0 taken 1545 times.
✓ Branch 1 taken 4553 times.
|
6098 | if (alien_cache_) { |
236 |
3/4✓ Branch 1 taken 1545 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 1496 times.
|
1545 | if (!MakeCacheDirectories(cache_path, 0770)) { |
237 | 49 | return false; | |
238 | } | ||
239 |
1/2✓ Branch 1 taken 1496 times.
✗ Branch 2 not taken.
|
1496 | LogCvmfs(kLogCache, kLogDebug | kLogSyslog, |
240 | "Cache directory structure created."); | ||
241 |
1/3✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1496 times.
|
1496 | switch (fs_info.type) { |
242 | ✗ | case kFsTypeNFS: | |
243 | ✗ | rename_workaround_ = kRenameLink; | |
244 | ✗ | LogCvmfs(kLogCache, kLogDebug | kLogSyslog, "Alien cache is on NFS."); | |
245 | ✗ | break; | |
246 | ✗ | case kFsTypeBeeGFS: | |
247 | ✗ | rename_workaround_ = kRenameSamedir; | |
248 | ✗ | LogCvmfs(kLogCache, kLogDebug | kLogSyslog, | |
249 | "Alien cache is on BeeGFS."); | ||
250 | ✗ | break; | |
251 | 1496 | default: | |
252 | 1496 | break; | |
253 | } | ||
254 | } else { | ||
255 |
3/4✓ Branch 1 taken 4553 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 4504 times.
|
4553 | if (!MakeCacheDirectories(cache_path, 0700)) |
256 | 49 | return false; | |
257 | } | ||
258 | |||
259 | // TODO(jblomer): we might not need to look anymore for cvmfs 2.0 relicts | ||
260 |
4/7✓ Branch 1 taken 6000 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6000 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49 times.
✓ Branch 8 taken 5951 times.
|
6000 | if (FileExists(cache_path + "/cvmfscatalog.cache")) { |
261 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
49 | LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr, |
262 | "Not mounting on cvmfs 2.0.X cache"); | ||
263 | 49 | return false; | |
264 | } | ||
265 | 5951 | return true; | |
266 | } | ||
267 | |||
268 | 6098 | PosixCacheManager *PosixCacheManager::Create( | |
269 | const string &cache_path, | ||
270 | const bool alien_cache, | ||
271 | const RenameWorkarounds rename_workaround, | ||
272 | const bool do_refcount) { | ||
273 | UniquePtr<PosixCacheManager> cache_manager( | ||
274 |
3/6✓ Branch 1 taken 6098 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6098 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6098 times.
✗ Branch 8 not taken.
|
6098 | new PosixCacheManager(cache_path, alien_cache, do_refcount)); |
275 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6098 times.
|
6098 | assert(cache_manager.IsValid()); |
276 | |||
277 | 6098 | cache_manager->rename_workaround_ = rename_workaround; | |
278 | |||
279 |
1/2✓ Branch 2 taken 6098 times.
✗ Branch 3 not taken.
|
6098 | const bool result_ = cache_manager->InitCacheDirectory(cache_path); |
280 |
2/2✓ Branch 0 taken 147 times.
✓ Branch 1 taken 5951 times.
|
6098 | if (!result_) { |
281 | 147 | return NULL; | |
282 | } | ||
283 | |||
284 | 5951 | return cache_manager.Release(); | |
285 | 6098 | } | |
286 | |||
287 | |||
288 | 4674 | void PosixCacheManager::CtrlTxn(const Label &label, | |
289 | const int flags, | ||
290 | void *txn) { | ||
291 | 4674 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
292 | 4674 | transaction->label = label; | |
293 | 4674 | } | |
294 | |||
295 | |||
296 | ✗ | string PosixCacheManager::Describe() { | |
297 | ✗ | string msg; | |
298 | ✗ | if (do_refcount_) { | |
299 | msg = "Refcounting Posix cache manager" | ||
300 | "(cache directory: " | ||
301 | ✗ | + cache_path_ + ")\n"; | |
302 | } else { | ||
303 | ✗ | msg = "Posix cache manager (cache directory: " + cache_path_ + ")\n"; | |
304 | } | ||
305 | ✗ | return msg; | |
306 | } | ||
307 | |||
308 | |||
309 | /** | ||
310 | * If not refcounting, nothing to do, the kernel keeps the state | ||
311 | * of open file descriptors. Return a dummy memory location. | ||
312 | */ | ||
313 | 46 | void *PosixCacheManager::DoSaveState() { | |
314 |
1/2✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
|
46 | if (do_refcount_) { |
315 |
1/2✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
|
46 | SavedState *state = new SavedState(); |
316 | 46 | state->fd_mgr = fd_mgr_->Clone(); | |
317 | 46 | return state; | |
318 | } | ||
319 | ✗ | char *c = reinterpret_cast<char *>(smalloc(1)); | |
320 | ✗ | *c = kMagicNoRefcount; | |
321 | ✗ | return c; | |
322 | } | ||
323 | |||
324 | |||
325 | 46 | int PosixCacheManager::DoRestoreState(void *data) { | |
326 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
|
46 | assert(data); |
327 |
1/2✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
|
46 | if (do_refcount_) { |
328 | 46 | SavedState *state = reinterpret_cast<SavedState *>(data); | |
329 |
1/2✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
|
46 | if (state->magic_number == kMagicRefcount) { |
330 | 46 | LogCvmfs(kLogCache, kLogDebug, | |
331 | "Restoring refcount cache manager from " | ||
332 | "refcounted posix cache manager"); | ||
333 | |||
334 | 46 | fd_mgr_->AssignFrom(state->fd_mgr.weak_ref()); | |
335 | } else { | ||
336 | ✗ | LogCvmfs(kLogCache, kLogDebug, | |
337 | "Restoring refcount cache manager from " | ||
338 | "non-refcounted posix cache manager"); | ||
339 | } | ||
340 | 46 | return -1; | |
341 | } | ||
342 | |||
343 | ✗ | char *c = reinterpret_cast<char *>(data); | |
344 | ✗ | assert(*c == kMagicNoRefcount || *c == kMagicRefcount); | |
345 | ✗ | if (*c == kMagicRefcount) { | |
346 | ✗ | SavedState *state = reinterpret_cast<SavedState *>(data); | |
347 | ✗ | LogCvmfs(kLogCache, kLogDebug, | |
348 | "Restoring non-refcount cache manager from " | ||
349 | "refcounted posix cache manager - this " | ||
350 | " is not possible, keep refcounting."); | ||
351 | ✗ | fd_mgr_->AssignFrom(state->fd_mgr.weak_ref()); | |
352 | ✗ | do_refcount_ = true; | |
353 | } | ||
354 | ✗ | return -1; | |
355 | } | ||
356 | |||
357 | |||
358 | 46 | bool PosixCacheManager::DoFreeState(void *data) { | |
359 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
|
46 | assert(data); |
360 | 46 | SavedState *state = reinterpret_cast<SavedState *>(data); | |
361 |
1/2✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
|
46 | if (state->magic_number == kMagicRefcount) { |
362 |
1/2✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
|
46 | delete state; |
363 | } else { | ||
364 | // If not refcounted, the state is the dummy SavedState | ||
365 | // of the regular posix cache manager | ||
366 | ✗ | free(data); | |
367 | } | ||
368 | 46 | return true; | |
369 | } | ||
370 | |||
371 | |||
372 | 94 | int PosixCacheManager::Dup(int fd) { | |
373 |
1/2✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
|
94 | const int new_fd = do_refcount_ ? fd_mgr_->Dup(fd) : dup(fd); |
374 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 47 times.
|
94 | if (new_fd < 0) |
375 | 47 | return -errno; | |
376 | 47 | return new_fd; | |
377 | } | ||
378 | |||
379 | |||
380 | 9263 | int PosixCacheManager::Flush(Transaction *transaction) { | |
381 |
2/2✓ Branch 0 taken 2208 times.
✓ Branch 1 taken 7055 times.
|
9263 | if (transaction->buf_pos == 0) |
382 | 2208 | return 0; | |
383 | 14110 | const int written = write(transaction->fd, transaction->buffer, | |
384 | 7055 | transaction->buf_pos); | |
385 |
2/2✓ Branch 0 taken 147 times.
✓ Branch 1 taken 6908 times.
|
7055 | if (written < 0) |
386 | 147 | return -errno; | |
387 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6908 times.
|
6908 | if (static_cast<unsigned>(written) != transaction->buf_pos) { |
388 | ✗ | transaction->buf_pos -= written; | |
389 | ✗ | return -EIO; | |
390 | } | ||
391 | 6908 | transaction->buf_pos = 0; | |
392 | 6908 | return 0; | |
393 | } | ||
394 | |||
395 | |||
396 | 9161 | inline string PosixCacheManager::GetPathInCache(const shash::Any &id) { | |
397 |
2/4✓ Branch 2 taken 9161 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9161 times.
✗ Branch 6 not taken.
|
18322 | return cache_path_ + "/" + id.MakePathWithoutSuffix(); |
398 | } | ||
399 | |||
400 | |||
401 | 1824 | int64_t PosixCacheManager::GetSize(int fd) { | |
402 | platform_stat64 info; | ||
403 | 1824 | const int retval = platform_fstat(fd, &info); | |
404 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1775 times.
|
1824 | if (retval != 0) |
405 | 49 | return -errno; | |
406 | 1775 | return info.st_size; | |
407 | } | ||
408 | |||
409 | |||
410 | 3172 | int PosixCacheManager::Open(const LabeledObject &object) { | |
411 |
1/2✓ Branch 1 taken 3172 times.
✗ Branch 2 not taken.
|
3172 | const string path = GetPathInCache(object.id); |
412 | int result; | ||
413 |
1/2✓ Branch 0 taken 3172 times.
✗ Branch 1 not taken.
|
3172 | if (do_refcount_) { |
414 |
1/2✓ Branch 2 taken 3172 times.
✗ Branch 3 not taken.
|
3172 | result = fd_mgr_->Open(object.id, path); |
415 | } else { | ||
416 | ✗ | result = open(path.c_str(), O_RDONLY); | |
417 | } | ||
418 |
2/2✓ Branch 0 taken 1249 times.
✓ Branch 1 taken 1923 times.
|
3172 | if (result >= 0) { |
419 |
1/2✓ Branch 2 taken 1249 times.
✗ Branch 3 not taken.
|
1249 | LogCvmfs(kLogCache, kLogDebug, "hit %s", path.c_str()); |
420 | // platform_disable_kcache(result); | ||
421 |
1/2✓ Branch 1 taken 1249 times.
✗ Branch 2 not taken.
|
1249 | quota_mgr_->Touch(object.id); |
422 | } else { | ||
423 | 1923 | result = -errno; | |
424 |
1/2✓ Branch 2 taken 1923 times.
✗ Branch 3 not taken.
|
1923 | LogCvmfs(kLogCache, kLogDebug, "miss %s (%d)", path.c_str(), result); |
425 | } | ||
426 | 3172 | return result; | |
427 | 3172 | } | |
428 | |||
429 | |||
430 | 801 | int PosixCacheManager::OpenFromTxn(void *txn) { | |
431 | 801 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
432 | 801 | const int retval = Flush(transaction); | |
433 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 752 times.
|
801 | if (retval < 0) |
434 | 49 | return retval; | |
435 | int fd_rdonly; | ||
436 | |||
437 |
1/2✓ Branch 0 taken 752 times.
✗ Branch 1 not taken.
|
752 | if (do_refcount_) { |
438 |
2/4✓ Branch 4 taken 752 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 752 times.
✗ Branch 8 not taken.
|
752 | fd_rdonly = fd_mgr_->Open(transaction->id, transaction->tmp_path.c_str()); |
439 | } else { | ||
440 | ✗ | fd_rdonly = open(transaction->tmp_path.c_str(), O_RDONLY); | |
441 | } | ||
442 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 703 times.
|
752 | if (fd_rdonly == -1) |
443 | 49 | return -errno; | |
444 | 703 | return fd_rdonly; | |
445 | } | ||
446 | |||
447 | |||
448 | 12745 | int64_t PosixCacheManager::Pread(int fd, | |
449 | void *buf, | ||
450 | uint64_t size, | ||
451 | uint64_t offset) { | ||
452 | int64_t result; | ||
453 | do { | ||
454 | 12745 | errno = 0; | |
455 | 12745 | result = pread(fd, buf, size, offset); | |
456 |
3/4✓ Branch 0 taken 98 times.
✓ Branch 1 taken 12647 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 98 times.
|
12745 | } while ((result == -1) && (errno == EINTR)); |
457 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 12647 times.
|
12745 | if (result < 0) |
458 | 98 | return -errno; | |
459 | 12647 | return result; | |
460 | } | ||
461 | |||
462 | |||
463 | 5399 | int PosixCacheManager::Rename(const char *oldpath, const char *newpath) { | |
464 | int result; | ||
465 |
2/2✓ Branch 0 taken 5252 times.
✓ Branch 1 taken 147 times.
|
5399 | if (rename_workaround_ != kRenameLink) { |
466 | 5252 | result = rename(oldpath, newpath); | |
467 |
2/2✓ Branch 0 taken 147 times.
✓ Branch 1 taken 5105 times.
|
5252 | if (result < 0) |
468 | 147 | return -errno; | |
469 | 5105 | return 0; | |
470 | } | ||
471 | |||
472 | 147 | result = link(oldpath, newpath); | |
473 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 49 times.
|
147 | if (result < 0) { |
474 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 49 times.
|
98 | if (errno == EEXIST) { |
475 | 49 | LogCvmfs(kLogCache, kLogDebug, "%s already existed, ignoring", newpath); | |
476 | } else { | ||
477 | 49 | return -errno; | |
478 | } | ||
479 | } | ||
480 | 98 | result = unlink(oldpath); | |
481 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
|
98 | if (result < 0) |
482 | ✗ | return -errno; | |
483 | 98 | return 0; | |
484 | } | ||
485 | |||
486 | |||
487 | /** | ||
488 | * Used by the sqlite vfs in order to preload file catalogs into the file system | ||
489 | * buffers. | ||
490 | * | ||
491 | * No-op if the fd is to a file that is on a tmpfs, and so already in page cache | ||
492 | */ | ||
493 | 785 | int PosixCacheManager::Readahead(int fd) { | |
494 | unsigned char *buf[4096]; | ||
495 | int nbytes; | ||
496 | 785 | uint64_t pos = 0; | |
497 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 785 times.
|
785 | if (is_tmpfs()) { |
498 | ✗ | return 0; | |
499 | } | ||
500 | do { | ||
501 |
1/2✓ Branch 1 taken 3757 times.
✗ Branch 2 not taken.
|
3757 | nbytes = Pread(fd, buf, 4096, pos); |
502 | 3757 | pos += nbytes; | |
503 |
2/2✓ Branch 0 taken 2972 times.
✓ Branch 1 taken 785 times.
|
3757 | } while (nbytes == 4096); |
504 |
1/2✓ Branch 1 taken 785 times.
✗ Branch 2 not taken.
|
785 | LogCvmfs(kLogCache, kLogDebug, "read-ahead %d, %" PRIu64, fd, pos); |
505 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 785 times.
|
785 | if (nbytes < 0) |
506 | ✗ | return nbytes; | |
507 | 785 | return 0; | |
508 | } | ||
509 | |||
510 | |||
511 | 153 | int PosixCacheManager::Reset(void *txn) { | |
512 | 153 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
513 | 153 | transaction->buf_pos = 0; | |
514 | 153 | transaction->size = 0; | |
515 | 153 | int retval = lseek(transaction->fd, 0, SEEK_SET); | |
516 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 104 times.
|
153 | if (retval < 0) |
517 | 49 | return -errno; | |
518 | 104 | retval = ftruncate(transaction->fd, 0); | |
519 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
|
104 | if (retval < 0) |
520 | ✗ | return -errno; | |
521 | 104 | return 0; | |
522 | } | ||
523 | |||
524 | |||
525 | 6087 | int PosixCacheManager::StartTxn(const shash::Any &id, | |
526 | uint64_t size, | ||
527 | void *txn) { | ||
528 | 6087 | atomic_inc32(&no_inflight_txns_); | |
529 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 6038 times.
|
6087 | if (cache_mode_ == kCacheReadOnly) { |
530 | 49 | atomic_dec32(&no_inflight_txns_); | |
531 | 49 | return -EROFS; | |
532 | } | ||
533 | |||
534 |
2/2✓ Branch 0 taken 5382 times.
✓ Branch 1 taken 656 times.
|
6038 | if (size != kSizeUnknown) { |
535 |
3/4✓ Branch 1 taken 5382 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 5333 times.
|
5382 | if (size > quota_mgr_->GetMaxFileSize()) { |
536 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
49 | LogCvmfs(kLogCache, kLogDebug, |
537 | "file too big for lru cache (%" PRIu64 " " | ||
538 | "requested but only %" PRIu64 " bytes free)", | ||
539 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
49 | size, quota_mgr_->GetMaxFileSize()); |
540 | 49 | atomic_dec32(&no_inflight_txns_); | |
541 | 49 | return -ENOSPC; | |
542 | } | ||
543 | |||
544 | // For large files, ensure enough free cache space before writing the chunk | ||
545 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 5284 times.
|
5333 | if (size > kBigFile) { |
546 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
49 | const uint64_t cache_size = quota_mgr_->GetSize(); |
547 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
49 | const uint64_t cache_capacity = quota_mgr_->GetCapacity(); |
548 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
|
49 | assert(cache_capacity >= size); |
549 |
1/2✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
|
49 | if ((cache_size + size) > cache_capacity) { |
550 | 98 | const uint64_t leave_size = std::min(cache_capacity / 2, | |
551 | 49 | cache_capacity - size); | |
552 |
1/2✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
|
49 | quota_mgr_->Cleanup(leave_size); |
553 | } | ||
554 | } | ||
555 | } | ||
556 | |||
557 |
1/2✓ Branch 1 taken 5989 times.
✗ Branch 2 not taken.
|
5989 | string path_in_cache = GetPathInCache(id); |
558 |
1/2✓ Branch 2 taken 5989 times.
✗ Branch 3 not taken.
|
5989 | Transaction *transaction = new (txn) Transaction(id, path_in_cache); |
559 | |||
560 | 5989 | char *template_path = NULL; | |
561 | 5989 | unsigned temp_path_len = 0; | |
562 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 5891 times.
|
5989 | if (rename_workaround_ == kRenameSamedir) { |
563 | 98 | temp_path_len = path_in_cache.length() + 6; | |
564 | 98 | template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1)); | |
565 | 98 | memcpy(template_path, path_in_cache.data(), path_in_cache.length()); | |
566 | 98 | memset(template_path + path_in_cache.length(), 'X', 6); | |
567 | } else { | ||
568 | 5891 | temp_path_len = txn_template_path_.length(); | |
569 | 5891 | template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1)); | |
570 |
1/2✓ Branch 1 taken 5891 times.
✗ Branch 2 not taken.
|
5891 | memcpy(template_path, &txn_template_path_[0], temp_path_len); |
571 | } | ||
572 | 5989 | template_path[temp_path_len] = '\0'; | |
573 | |||
574 |
1/2✓ Branch 1 taken 5989 times.
✗ Branch 2 not taken.
|
5989 | transaction->fd = mkstemp(template_path); |
575 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 5939 times.
|
5989 | if (transaction->fd == -1) { |
576 | 50 | transaction->~Transaction(); | |
577 | 50 | atomic_dec32(&no_inflight_txns_); | |
578 | 50 | return -errno; | |
579 | } | ||
580 | |||
581 |
1/2✓ Branch 1 taken 5939 times.
✗ Branch 2 not taken.
|
5939 | LogCvmfs(kLogCache, kLogDebug, "start transaction on %s has result %d", |
582 | template_path, transaction->fd); | ||
583 |
1/2✓ Branch 1 taken 5939 times.
✗ Branch 2 not taken.
|
5939 | transaction->tmp_path = template_path; |
584 | 5939 | transaction->expected_size = size; | |
585 | 5939 | return transaction->fd; | |
586 | 5989 | } | |
587 | |||
588 | |||
589 | 343 | manifest::Breadcrumb PosixCacheManager::LoadBreadcrumb( | |
590 | const std::string &fqrn) { | ||
591 | 343 | return manifest::Manifest::ReadBreadcrumb(fqrn, cache_path_); | |
592 | } | ||
593 | |||
594 | |||
595 | 111 | bool PosixCacheManager::StoreBreadcrumb(const manifest::Manifest &manifest) { | |
596 | 111 | return manifest.ExportBreadcrumb(cache_path_, 0600); | |
597 | } | ||
598 | |||
599 | |||
600 | ✗ | bool PosixCacheManager::StoreBreadcrumb(std::string fqrn, | |
601 | manifest::Breadcrumb breadcrumb) { | ||
602 | ✗ | return breadcrumb.Export(fqrn, cache_path_, 0600); | |
603 | } | ||
604 | |||
605 | |||
606 | 196 | void PosixCacheManager::TearDown2ReadOnly() { | |
607 | 196 | cache_mode_ = kCacheReadOnly; | |
608 |
2/2✓ Branch 1 taken 686 times.
✓ Branch 2 taken 147 times.
|
833 | while (atomic_read32(&no_inflight_txns_) != 0) |
609 | 686 | SafeSleepMs(50); | |
610 | |||
611 | 147 | QuotaManager *old_manager = quota_mgr_; | |
612 |
1/2✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
|
147 | quota_mgr_ = new NoopQuotaManager(); |
613 |
1/2✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
|
147 | delete old_manager; |
614 | 147 | } | |
615 | |||
616 | |||
617 | 5979 | int64_t PosixCacheManager::Write(const void *buf, uint64_t size, void *txn) { | |
618 | 5979 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
619 | |||
620 |
2/2✓ Branch 0 taken 4800 times.
✓ Branch 1 taken 1179 times.
|
5979 | if (transaction->expected_size != kSizeUnknown) { |
621 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 4751 times.
|
4800 | if (transaction->size + size > transaction->expected_size) { |
622 | 49 | LogCvmfs(kLogCache, kLogDebug, | |
623 | "Transaction size (%" PRIu64 ") > expected size (%" PRIu64 ")", | ||
624 | 49 | transaction->size + size, transaction->expected_size); | |
625 | 49 | return -EFBIG; | |
626 | } | ||
627 | } | ||
628 | |||
629 | 5930 | uint64_t written = 0; | |
630 | 5930 | const unsigned char *read_pos = reinterpret_cast<const unsigned char *>(buf); | |
631 |
2/2✓ Branch 0 taken 7204 times.
✓ Branch 1 taken 5881 times.
|
13085 | while (written < size) { |
632 |
2/2✓ Branch 0 taken 3208 times.
✓ Branch 1 taken 3996 times.
|
7204 | if (transaction->buf_pos == sizeof(transaction->buffer)) { |
633 |
1/2✓ Branch 1 taken 3208 times.
✗ Branch 2 not taken.
|
3208 | const int retval = Flush(transaction); |
634 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 3159 times.
|
3208 | if (retval != 0) { |
635 | 49 | transaction->size += written; | |
636 | 49 | return retval; | |
637 | } | ||
638 | } | ||
639 | 7155 | const uint64_t remaining = size - written; | |
640 | 7155 | const uint64_t space_in_buffer = sizeof(transaction->buffer) | |
641 | 7155 | - transaction->buf_pos; | |
642 | 7155 | const uint64_t batch_size = std::min(remaining, space_in_buffer); | |
643 | 7155 | memcpy(transaction->buffer + transaction->buf_pos, read_pos, batch_size); | |
644 | 7155 | transaction->buf_pos += batch_size; | |
645 | 7155 | written += batch_size; | |
646 | 7155 | read_pos += batch_size; | |
647 | } | ||
648 | 5881 | transaction->size += written; | |
649 | 5881 | return written; | |
650 | } | ||
651 |