GCC Code Coverage Report


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