GCC Code Coverage Report


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