Directory: | cvmfs/ |
---|---|
File: | cvmfs/cache_posix.cc |
Date: | 2025-03-09 02:34:28 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 282 | 320 | 88.1% |
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 | 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 | private: | ||
97 | bool drainout_; | ||
98 | static atomic_int32 global_drainout_; | ||
99 | static atomic_int32 num_inflight_calls_; | ||
100 | }; | ||
101 | atomic_int32 CallGuard::num_inflight_calls_ = 0; | ||
102 | atomic_int32 CallGuard::global_drainout_ = 0; | ||
103 | |||
104 | } // anonymous namespace | ||
105 | |||
106 | |||
107 | //------------------------------------------------------------------------------ | ||
108 | |||
109 | |||
110 | const uint64_t PosixCacheManager::kBigFile = 25 * 1024 * 1024; // 25M | ||
111 | |||
112 | |||
113 | 19 | int PosixCacheManager::AbortTxn(void *txn) { | |
114 | 19 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
115 | 19 | LogCvmfs(kLogCache, kLogDebug, "abort %s", transaction->tmp_path.c_str()); | |
116 | 19 | close(transaction->fd); | |
117 | 19 | int result = unlink(transaction->tmp_path.c_str()); | |
118 | 19 | transaction->~Transaction(); | |
119 | 19 | atomic_dec32(&no_inflight_txns_); | |
120 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
|
19 | if (result == -1) |
121 | 2 | return -errno; | |
122 | 17 | return 0; | |
123 | } | ||
124 | |||
125 | |||
126 | /** | ||
127 | * This should only be used to replace the default NoopQuotaManager by a | ||
128 | * PosixQuotaManager. The cache manager takes the ownership of the passed | ||
129 | * quota manager. | ||
130 | */ | ||
131 | 33 | bool PosixCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) { | |
132 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | if (quota_mgr == NULL) |
133 | ✗ | return false; | |
134 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | delete quota_mgr_; |
135 | 33 | quota_mgr_ = quota_mgr; | |
136 | 33 | return true; | |
137 | } | ||
138 | |||
139 | |||
140 | 78 | int PosixCacheManager::Close(int fd) { | |
141 |
1/2✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
|
78 | int retval = do_refcount_ ? fd_mgr_->Close(fd) : close(fd); |
142 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 77 times.
|
78 | if (retval != 0) |
143 | 1 | return -errno; | |
144 | 77 | return 0; | |
145 | } | ||
146 | |||
147 | |||
148 | 139 | int PosixCacheManager::CommitTxn(void *txn) { | |
149 | 139 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
150 | int result; | ||
151 | 139 | LogCvmfs(kLogCache, kLogDebug, "commit %s %s", | |
152 | transaction->final_path.c_str(), transaction->tmp_path.c_str()); | ||
153 | |||
154 | 139 | result = Flush(transaction); | |
155 | 139 | close(transaction->fd); | |
156 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 138 times.
|
139 | if (result < 0) { |
157 | 1 | unlink(transaction->tmp_path.c_str()); | |
158 | 1 | transaction->~Transaction(); | |
159 | 1 | atomic_dec32(&no_inflight_txns_); | |
160 | 1 | return result; | |
161 | } | ||
162 | |||
163 | // To support debugging, move files into quarantine on file size mismatch | ||
164 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 105 times.
|
138 | if (transaction->size != transaction->expected_size) { |
165 | // Allow size to be zero if alien cache, because hadoop-fuse-dfs returns | ||
166 | // size zero for a while | ||
167 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 30 times.
|
33 | if ( (transaction->expected_size != kSizeUnknown) && |
168 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3 | (reports_correct_filesize_ || (transaction->size != 0)) ) |
169 | { | ||
170 |
1/3✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
3 | LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr, |
171 | "size check failure for %s, expected %lu, got %lu", | ||
172 | 6 | transaction->id.ToString().c_str(), | |
173 | transaction->expected_size, transaction->size); | ||
174 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | CopyPath2Path(transaction->tmp_path, |
175 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
6 | cache_path_ + "/quarantaine/" + transaction->id.ToString()); |
176 | 3 | unlink(transaction->tmp_path.c_str()); | |
177 | 3 | transaction->~Transaction(); | |
178 | 3 | atomic_dec32(&no_inflight_txns_); | |
179 | 3 | return -EIO; | |
180 | } | ||
181 | } | ||
182 | |||
183 |
2/2✓ Branch 0 taken 134 times.
✓ Branch 1 taken 1 times.
|
135 | if ((transaction->label.flags & kLabelPinned) || |
184 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 105 times.
|
134 | (transaction->label.flags & kLabelCatalog)) |
185 | { | ||
186 | 90 | bool retval = quota_mgr_->Pin( | |
187 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | transaction->id, transaction->size, transaction->label.GetDescription(), |
188 | 30 | (transaction->label.flags & kLabelCatalog)); | |
189 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29 times.
|
30 | if (!retval) { |
190 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | LogCvmfs(kLogCache, kLogDebug, "commit failed: cannot pin %s", |
191 | 2 | transaction->id.ToString().c_str()); | |
192 | 1 | unlink(transaction->tmp_path.c_str()); | |
193 | 1 | transaction->~Transaction(); | |
194 | 1 | atomic_dec32(&no_inflight_txns_); | |
195 | 1 | return -ENOSPC; | |
196 | } | ||
197 | } | ||
198 | |||
199 | // Move the temporary file into its final location | ||
200 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 133 times.
|
134 | if (alien_cache_) { |
201 | 1 | int retval = chmod(transaction->tmp_path.c_str(), 0660); | |
202 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(retval == 0); |
203 | } | ||
204 | result = | ||
205 | 134 | Rename(transaction->tmp_path.c_str(), transaction->final_path.c_str()); | |
206 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 132 times.
|
134 | if (result < 0) { |
207 | 2 | LogCvmfs(kLogCache, kLogDebug, "commit failed: %s", strerror(errno)); | |
208 | 2 | unlink(transaction->tmp_path.c_str()); | |
209 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if ((transaction->label.flags & kLabelPinned) || |
210 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | (transaction->label.flags & kLabelCatalog)) |
211 | { | ||
212 | 1 | quota_mgr_->Remove(transaction->id); | |
213 | } | ||
214 | } else { | ||
215 | // Success, inform quota manager | ||
216 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 131 times.
|
132 | if (transaction->label.flags & kLabelVolatile) { |
217 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | quota_mgr_->InsertVolatile(transaction->id, transaction->size, |
218 | 2 | transaction->label.GetDescription()); | |
219 |
4/4✓ Branch 1 taken 104 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 103 times.
✓ Branch 4 taken 28 times.
|
235 | } else if (!transaction->label.IsCatalog() && |
220 |
2/2✓ Branch 1 taken 103 times.
✓ Branch 2 taken 1 times.
|
104 | !transaction->label.IsPinned()) |
221 | { | ||
222 |
1/2✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
|
103 | quota_mgr_->Insert(transaction->id, transaction->size, |
223 | 206 | transaction->label.GetDescription()); | |
224 | } | ||
225 | } | ||
226 | 134 | transaction->~Transaction(); | |
227 | 134 | atomic_dec32(&no_inflight_txns_); | |
228 | 134 | return result; | |
229 | } | ||
230 | |||
231 | 171 | bool PosixCacheManager::InitCacheDirectory(const string &cache_path) { | |
232 |
1/2✓ Branch 1 taken 171 times.
✗ Branch 2 not taken.
|
171 | FileSystemInfo fs_info = GetFileSystemInfo(cache_path); |
233 | |||
234 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 169 times.
|
171 | if (fs_info.type == kFsTypeTmpfs) { |
235 | 2 | is_tmpfs_ = true; | |
236 | } | ||
237 | |||
238 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 135 times.
|
171 | if (alien_cache_) { |
239 |
3/4✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 35 times.
|
36 | if (!MakeCacheDirectories(cache_path, 0770)) { |
240 | 1 | return false; | |
241 | } | ||
242 |
1/2✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
|
35 | LogCvmfs(kLogCache, kLogDebug | kLogSyslog, |
243 | "Cache directory structure created."); | ||
244 |
1/3✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
|
35 | switch (fs_info.type) { |
245 | ✗ | case kFsTypeNFS: | |
246 | ✗ | rename_workaround_ = kRenameLink; | |
247 | ✗ | LogCvmfs(kLogCache, kLogDebug | kLogSyslog, | |
248 | "Alien cache is on NFS."); | ||
249 | ✗ | break; | |
250 | ✗ | case kFsTypeBeeGFS: | |
251 | ✗ | rename_workaround_ = kRenameSamedir; | |
252 | ✗ | LogCvmfs(kLogCache, kLogDebug | kLogSyslog, | |
253 | "Alien cache is on BeeGFS."); | ||
254 | ✗ | break; | |
255 | 35 | default: | |
256 | 35 | break; | |
257 | } | ||
258 | } else { | ||
259 |
3/4✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 134 times.
|
135 | if (!MakeCacheDirectories(cache_path, 0700)) |
260 | 1 | return false; | |
261 | } | ||
262 | |||
263 | // TODO(jblomer): we might not need to look anymore for cvmfs 2.0 relicts | ||
264 |
4/7✓ Branch 1 taken 169 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 169 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 168 times.
|
169 | if (FileExists(cache_path + "/cvmfscatalog.cache")) { |
265 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr, |
266 | "Not mounting on cvmfs 2.0.X cache"); | ||
267 | 1 | return false; | |
268 | } | ||
269 | 168 | return true; | |
270 | } | ||
271 | |||
272 | 171 | PosixCacheManager *PosixCacheManager::Create( | |
273 | const string &cache_path, | ||
274 | const bool alien_cache, | ||
275 | const RenameWorkarounds rename_workaround, | ||
276 | const bool do_refcount) | ||
277 | { | ||
278 | UniquePtr<PosixCacheManager> cache_manager( | ||
279 |
3/6✓ Branch 1 taken 171 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 171 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 171 times.
✗ Branch 8 not taken.
|
171 | new PosixCacheManager(cache_path, alien_cache, do_refcount)); |
280 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 171 times.
|
171 | assert(cache_manager.IsValid()); |
281 | |||
282 | 171 | cache_manager->rename_workaround_ = rename_workaround; | |
283 | |||
284 |
1/2✓ Branch 2 taken 171 times.
✗ Branch 3 not taken.
|
171 | bool result_ = cache_manager->InitCacheDirectory(cache_path); |
285 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 168 times.
|
171 | if (!result_) { |
286 | 3 | return NULL; | |
287 | } | ||
288 | |||
289 | 168 | return cache_manager.Release(); | |
290 | 171 | } | |
291 | |||
292 | |||
293 | 132 | void PosixCacheManager::CtrlTxn( | |
294 | const Label &label, | ||
295 | const int flags, | ||
296 | void *txn) | ||
297 | { | ||
298 | 132 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
299 | 132 | transaction->label = label; | |
300 | 132 | } | |
301 | |||
302 | |||
303 | ✗ | string PosixCacheManager::Describe() { | |
304 | ✗ | string msg; | |
305 | ✗ | if (do_refcount_) { | |
306 | msg = "Refcounting Posix cache manager" | ||
307 | ✗ | "(cache directory: " + cache_path_ + ")\n"; | |
308 | } else { | ||
309 | ✗ | msg = "Posix cache manager (cache directory: " + cache_path_ + ")\n"; | |
310 | } | ||
311 | ✗ | return msg; | |
312 | } | ||
313 | |||
314 | |||
315 | /** | ||
316 | * If not refcounting, nothing to do, the kernel keeps the state | ||
317 | * of open file descriptors. Return a dummy memory location. | ||
318 | */ | ||
319 | 1 | void *PosixCacheManager::DoSaveState() { | |
320 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (do_refcount_) { |
321 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | SavedState *state = new SavedState(); |
322 | 1 | state->fd_mgr = fd_mgr_->Clone(); | |
323 | 1 | return state; | |
324 | } | ||
325 | ✗ | char *c = reinterpret_cast<char *>(smalloc(1)); | |
326 | ✗ | *c = kMagicNoRefcount; | |
327 | ✗ | return c; | |
328 | } | ||
329 | |||
330 | |||
331 | 1 | int PosixCacheManager::DoRestoreState(void *data) { | |
332 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(data); |
333 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (do_refcount_) { |
334 | 1 | SavedState *state = reinterpret_cast<SavedState *>(data); | |
335 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (state->magic_number == kMagicRefcount) { |
336 | 1 | LogCvmfs(kLogCache, kLogDebug, "Restoring refcount cache manager from " | |
337 | "refcounted posix cache manager"); | ||
338 | |||
339 | 1 | fd_mgr_->AssignFrom(state->fd_mgr.weak_ref()); | |
340 | } else { | ||
341 | ✗ | LogCvmfs(kLogCache, kLogDebug, "Restoring refcount cache manager from " | |
342 | "non-refcounted posix cache manager"); | ||
343 | } | ||
344 | 1 | return -1; | |
345 | } | ||
346 | |||
347 | ✗ | char *c = reinterpret_cast<char *>(data); | |
348 | ✗ | assert(*c == kMagicNoRefcount || *c == kMagicRefcount); | |
349 | ✗ | if (*c == kMagicRefcount) { | |
350 | ✗ | SavedState *state = reinterpret_cast<SavedState *>(data); | |
351 | ✗ | LogCvmfs(kLogCache, kLogDebug, "Restoring non-refcount cache manager from " | |
352 | "refcounted posix cache manager - this " | ||
353 | " is not possible, keep refcounting."); | ||
354 | ✗ | fd_mgr_->AssignFrom(state->fd_mgr.weak_ref()); | |
355 | ✗ | do_refcount_ = true; | |
356 | } | ||
357 | ✗ | return -1; | |
358 | } | ||
359 | |||
360 | |||
361 | 1 | bool PosixCacheManager::DoFreeState(void *data) { | |
362 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(data); |
363 | 1 | SavedState *state = reinterpret_cast<SavedState *>(data); | |
364 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (state->magic_number == kMagicRefcount) { |
365 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | delete state; |
366 | } else { | ||
367 | // If not refcounted, the state is the dummy SavedState | ||
368 | // of the regular posix cache manager | ||
369 | ✗ | free(data); | |
370 | } | ||
371 | 1 | return true; | |
372 | } | ||
373 | |||
374 | |||
375 | |||
376 | 4 | int PosixCacheManager::Dup(int fd) { | |
377 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | int new_fd = do_refcount_ ? fd_mgr_->Dup(fd) : dup(fd); |
378 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if (new_fd < 0) |
379 | 2 | return -errno; | |
380 | 2 | return new_fd; | |
381 | } | ||
382 | |||
383 | |||
384 | 285 | int PosixCacheManager::Flush(Transaction *transaction) { | |
385 |
2/2✓ Branch 0 taken 68 times.
✓ Branch 1 taken 217 times.
|
285 | if (transaction->buf_pos == 0) |
386 | 68 | return 0; | |
387 | int written = | ||
388 | 217 | write(transaction->fd, transaction->buffer, transaction->buf_pos); | |
389 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 214 times.
|
217 | if (written < 0) |
390 | 3 | return -errno; | |
391 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 214 times.
|
214 | if (static_cast<unsigned>(written) != transaction->buf_pos) { |
392 | ✗ | transaction->buf_pos -= written; | |
393 | ✗ | return -EIO; | |
394 | } | ||
395 | 214 | transaction->buf_pos = 0; | |
396 | 214 | return 0; | |
397 | } | ||
398 | |||
399 | |||
400 | 309 | inline string PosixCacheManager::GetPathInCache(const shash::Any &id) { | |
401 |
2/4✓ Branch 2 taken 309 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 309 times.
✗ Branch 6 not taken.
|
618 | return cache_path_ + "/" + id.MakePathWithoutSuffix(); |
402 | } | ||
403 | |||
404 | |||
405 | 68 | int64_t PosixCacheManager::GetSize(int fd) { | |
406 | platform_stat64 info; | ||
407 | 68 | int retval = platform_fstat(fd, &info); | |
408 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 67 times.
|
68 | if (retval != 0) |
409 | 1 | return -errno; | |
410 | 67 | return info.st_size; | |
411 | } | ||
412 | |||
413 | |||
414 | 149 | int PosixCacheManager::Open(const LabeledObject &object) { | |
415 |
1/2✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
|
149 | const string path = GetPathInCache(object.id); |
416 | int result; | ||
417 |
1/2✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
|
149 | if (do_refcount_) { |
418 |
1/2✓ Branch 2 taken 149 times.
✗ Branch 3 not taken.
|
149 | result = fd_mgr_->Open(object.id, path); |
419 | } else { | ||
420 | ✗ | result = open(path.c_str(), O_RDONLY); | |
421 | } | ||
422 |
2/2✓ Branch 0 taken 46 times.
✓ Branch 1 taken 103 times.
|
149 | if (result >= 0) { |
423 |
1/2✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
|
46 | LogCvmfs(kLogCache, kLogDebug, "hit %s", path.c_str()); |
424 | // platform_disable_kcache(result); | ||
425 |
1/2✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
|
46 | quota_mgr_->Touch(object.id); |
426 | } else { | ||
427 | 103 | result = -errno; | |
428 |
1/2✓ Branch 2 taken 103 times.
✗ Branch 3 not taken.
|
103 | LogCvmfs(kLogCache, kLogDebug, "miss %s (%d)", path.c_str(), result); |
429 | } | ||
430 | 149 | return result; | |
431 | 149 | } | |
432 | |||
433 | |||
434 | 38 | int PosixCacheManager::OpenFromTxn(void *txn) { | |
435 | 38 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
436 | 38 | int retval = Flush(transaction); | |
437 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 37 times.
|
38 | if (retval < 0) |
438 | 1 | return retval; | |
439 | int fd_rdonly; | ||
440 | |||
441 |
1/2✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
|
37 | if (do_refcount_) { |
442 |
2/4✓ Branch 4 taken 37 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 37 times.
✗ Branch 8 not taken.
|
37 | fd_rdonly = fd_mgr_->Open(transaction->id, transaction->tmp_path.c_str()); |
443 | } else { | ||
444 | ✗ | fd_rdonly = open(transaction->tmp_path.c_str(), O_RDONLY); | |
445 | } | ||
446 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 36 times.
|
37 | if (fd_rdonly == -1) |
447 | 1 | return -errno; | |
448 | 36 | return fd_rdonly; | |
449 | } | ||
450 | |||
451 | |||
452 | 515 | int64_t PosixCacheManager::Pread( | |
453 | int fd, | ||
454 | void *buf, | ||
455 | uint64_t size, | ||
456 | uint64_t offset) | ||
457 | { | ||
458 | int64_t result; | ||
459 | do { | ||
460 | 515 | errno = 0; | |
461 | 515 | result = pread(fd, buf, size, offset); | |
462 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 513 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
515 | } while ((result == -1) && (errno == EINTR)); |
463 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 513 times.
|
515 | if (result < 0) |
464 | 2 | return -errno; | |
465 | 513 | return result; | |
466 | } | ||
467 | |||
468 | |||
469 | 140 | int PosixCacheManager::Rename(const char *oldpath, const char *newpath) { | |
470 | int result; | ||
471 |
2/2✓ Branch 0 taken 137 times.
✓ Branch 1 taken 3 times.
|
140 | if (rename_workaround_ != kRenameLink) { |
472 | 137 | result = rename(oldpath, newpath); | |
473 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 134 times.
|
137 | if (result < 0) |
474 | 3 | return -errno; | |
475 | 134 | return 0; | |
476 | } | ||
477 | |||
478 | 3 | result = link(oldpath, newpath); | |
479 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (result < 0) { |
480 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (errno == EEXIST) { |
481 | 1 | LogCvmfs(kLogCache, kLogDebug, "%s already existed, ignoring", newpath); | |
482 | } else { | ||
483 | 1 | return -errno; | |
484 | } | ||
485 | } | ||
486 | 2 | result = unlink(oldpath); | |
487 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (result < 0) |
488 | ✗ | return -errno; | |
489 | 2 | return 0; | |
490 | } | ||
491 | |||
492 | |||
493 | /** | ||
494 | * Used by the sqlite vfs in order to preload file catalogs into the file system | ||
495 | * buffers. | ||
496 | * | ||
497 | * No-op if the fd is to a file that is on a tmpfs, and so already in page cache | ||
498 | */ | ||
499 | 34 | int PosixCacheManager::Readahead(int fd) { | |
500 | unsigned char *buf[4096]; | ||
501 | int nbytes; | ||
502 | 34 | uint64_t pos = 0; | |
503 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
|
34 | if (is_tmpfs()) { |
504 | ✗ | return 0; | |
505 | } | ||
506 | do { | ||
507 |
1/2✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
|
162 | nbytes = Pread(fd, buf, 4096, pos); |
508 | 162 | pos += nbytes; | |
509 |
2/2✓ Branch 0 taken 128 times.
✓ Branch 1 taken 34 times.
|
162 | } while (nbytes == 4096); |
510 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | LogCvmfs(kLogCache, kLogDebug, "read-ahead %d, %" PRIu64, fd, pos); |
511 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
|
34 | if (nbytes < 0) |
512 | ✗ | return nbytes; | |
513 | 34 | return 0; | |
514 | } | ||
515 | |||
516 | |||
517 | 9 | int PosixCacheManager::Reset(void *txn) { | |
518 | 9 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
519 | 9 | transaction->buf_pos = 0; | |
520 | 9 | transaction->size = 0; | |
521 | 9 | int retval = lseek(transaction->fd, 0, SEEK_SET); | |
522 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
|
9 | if (retval < 0) |
523 | 1 | return -errno; | |
524 | 8 | retval = ftruncate(transaction->fd, 0); | |
525 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (retval < 0) |
526 | ✗ | return -errno; | |
527 | 8 | return 0; | |
528 | } | ||
529 | |||
530 | |||
531 | 162 | int PosixCacheManager::StartTxn( | |
532 | const shash::Any &id, | ||
533 | uint64_t size, | ||
534 | void *txn) | ||
535 | { | ||
536 | 162 | atomic_inc32(&no_inflight_txns_); | |
537 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 161 times.
|
162 | if (cache_mode_ == kCacheReadOnly) { |
538 | 1 | atomic_dec32(&no_inflight_txns_); | |
539 | 1 | return -EROFS; | |
540 | } | ||
541 | |||
542 |
2/2✓ Branch 0 taken 124 times.
✓ Branch 1 taken 37 times.
|
161 | if (size != kSizeUnknown) { |
543 |
3/4✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 123 times.
|
124 | if (size > quota_mgr_->GetMaxFileSize()) { |
544 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | LogCvmfs(kLogCache, kLogDebug, "file too big for lru cache (%" PRIu64 " " |
545 | "requested but only %" PRIu64 " bytes free)", | ||
546 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | size, quota_mgr_->GetMaxFileSize()); |
547 | 1 | atomic_dec32(&no_inflight_txns_); | |
548 | 1 | return -ENOSPC; | |
549 | } | ||
550 | |||
551 | // For large files, ensure enough free cache space before writing the chunk | ||
552 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 122 times.
|
123 | if (size > kBigFile) { |
553 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | uint64_t cache_size = quota_mgr_->GetSize(); |
554 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | uint64_t cache_capacity = quota_mgr_->GetCapacity(); |
555 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(cache_capacity >= size); |
556 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if ((cache_size + size) > cache_capacity) { |
557 | uint64_t leave_size = | ||
558 | 1 | std::min(cache_capacity / 2, cache_capacity - size); | |
559 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | quota_mgr_->Cleanup(leave_size); |
560 | } | ||
561 | } | ||
562 | } | ||
563 | |||
564 |
1/2✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
|
160 | string path_in_cache = GetPathInCache(id); |
565 |
1/2✓ Branch 2 taken 160 times.
✗ Branch 3 not taken.
|
160 | Transaction *transaction = new (txn) Transaction(id, path_in_cache); |
566 | |||
567 | 160 | char *template_path = NULL; | |
568 | 160 | unsigned temp_path_len = 0; | |
569 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 158 times.
|
160 | if (rename_workaround_ == kRenameSamedir) { |
570 | 2 | temp_path_len = path_in_cache.length() + 6; | |
571 | 2 | template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1)); | |
572 | 2 | memcpy(template_path, path_in_cache.data(), path_in_cache.length()); | |
573 | 2 | memset(template_path + path_in_cache.length(), 'X', 6); | |
574 | } else { | ||
575 | 158 | temp_path_len = txn_template_path_.length(); | |
576 | 158 | template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1)); | |
577 |
1/2✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
|
158 | memcpy(template_path, &txn_template_path_[0], temp_path_len); |
578 | } | ||
579 | 160 | template_path[temp_path_len] = '\0'; | |
580 | |||
581 |
1/2✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
|
160 | transaction->fd = mkstemp(template_path); |
582 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 158 times.
|
160 | if (transaction->fd == -1) { |
583 | 2 | transaction->~Transaction(); | |
584 | 2 | atomic_dec32(&no_inflight_txns_); | |
585 | 2 | return -errno; | |
586 | } | ||
587 | |||
588 |
1/2✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
|
158 | LogCvmfs(kLogCache, kLogDebug, "start transaction on %s has result %d", |
589 | template_path, transaction->fd); | ||
590 |
1/2✓ Branch 1 taken 158 times.
✗ Branch 2 not taken.
|
158 | transaction->tmp_path = template_path; |
591 | 158 | transaction->expected_size = size; | |
592 | 158 | return transaction->fd; | |
593 | 160 | } | |
594 | |||
595 | |||
596 | 24 | manifest::Breadcrumb PosixCacheManager::LoadBreadcrumb(const std::string &fqrn) | |
597 | { | ||
598 | 24 | return manifest::Manifest::ReadBreadcrumb(fqrn, cache_path_); | |
599 | } | ||
600 | |||
601 | |||
602 | 11 | bool PosixCacheManager::StoreBreadcrumb(const manifest::Manifest &manifest) { | |
603 | 11 | return manifest.ExportBreadcrumb(cache_path_, 0600); | |
604 | } | ||
605 | |||
606 | |||
607 | ✗ | bool PosixCacheManager::StoreBreadcrumb(std::string fqrn, | |
608 | manifest::Breadcrumb breadcrumb) { | ||
609 | ✗ | return breadcrumb.Export(fqrn, cache_path_, 0600); | |
610 | } | ||
611 | |||
612 | |||
613 | 4 | void PosixCacheManager::TearDown2ReadOnly() { | |
614 | 4 | cache_mode_ = kCacheReadOnly; | |
615 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 3 times.
|
18 | while (atomic_read32(&no_inflight_txns_) != 0) |
616 | 15 | SafeSleepMs(50); | |
617 | |||
618 | 3 | QuotaManager *old_manager = quota_mgr_; | |
619 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | quota_mgr_ = new NoopQuotaManager(); |
620 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | delete old_manager; |
621 | 3 | } | |
622 | |||
623 | |||
624 | 169 | int64_t PosixCacheManager::Write(const void *buf, uint64_t size, void *txn) { | |
625 | 169 | Transaction *transaction = reinterpret_cast<Transaction *>(txn); | |
626 | |||
627 |
2/2✓ Branch 0 taken 112 times.
✓ Branch 1 taken 57 times.
|
169 | if (transaction->expected_size != kSizeUnknown) { |
628 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 111 times.
|
112 | if (transaction->size + size > transaction->expected_size) { |
629 | 1 | LogCvmfs(kLogCache, kLogDebug, | |
630 | "Transaction size (%" PRIu64 ") > expected size (%" PRIu64 ")", | ||
631 | 1 | transaction->size + size, transaction->expected_size); | |
632 | 1 | return -EFBIG; | |
633 | } | ||
634 | } | ||
635 | |||
636 | 168 | uint64_t written = 0; | |
637 | 168 | const unsigned char *read_pos = reinterpret_cast<const unsigned char *>(buf); | |
638 |
2/2✓ Branch 0 taken 222 times.
✓ Branch 1 taken 167 times.
|
389 | while (written < size) { |
639 |
2/2✓ Branch 0 taken 108 times.
✓ Branch 1 taken 114 times.
|
222 | if (transaction->buf_pos == sizeof(transaction->buffer)) { |
640 |
1/2✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
|
108 | int retval = Flush(transaction); |
641 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 107 times.
|
108 | if (retval != 0) { |
642 | 1 | transaction->size += written; | |
643 | 1 | return retval; | |
644 | } | ||
645 | } | ||
646 | 221 | uint64_t remaining = size - written; | |
647 | 221 | uint64_t space_in_buffer = | |
648 | 221 | sizeof(transaction->buffer) - transaction->buf_pos; | |
649 | 221 | uint64_t batch_size = std::min(remaining, space_in_buffer); | |
650 | 221 | memcpy(transaction->buffer + transaction->buf_pos, read_pos, batch_size); | |
651 | 221 | transaction->buf_pos += batch_size; | |
652 | 221 | written += batch_size; | |
653 | 221 | read_pos += batch_size; | |
654 | } | ||
655 | 167 | transaction->size += written; | |
656 | 167 | return written; | |
657 | } | ||
658 |