GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_posix.cc
Date: 2026-05-24 02:35:55
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 264 int PosixCacheManager::AbortTxn(void *txn) {
104 264 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
105 264 LogCvmfs(kLogCache, kLogDebug, "abort %s", transaction->tmp_path.c_str());
106 264 close(transaction->fd);
107 264 const int result = unlink(transaction->tmp_path.c_str());
108 264 transaction->~Transaction();
109 264 atomic_dec32(&no_inflight_txns_);
110
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 241 times.
264 if (result == -1)
111 23 return -errno;
112 241 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 900 bool PosixCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) {
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 900 times.
900 if (quota_mgr == NULL)
123 return false;
124
1/2
✓ Branch 0 taken 900 times.
✗ Branch 1 not taken.
900 delete quota_mgr_;
125 900 quota_mgr_ = quota_mgr;
126 900 return true;
127 }
128
129
130 1620 int PosixCacheManager::Close(int fd) {
131
1/2
✓ Branch 0 taken 1620 times.
✗ Branch 1 not taken.
1620 const int retval = do_refcount_ ? fd_mgr_->Close(fd) : close(fd);
132
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1610 times.
1620 if (retval != 0)
133 10 return -errno;
134 1610 return 0;
135 }
136
137
138 2112 int PosixCacheManager::CommitTxn(void *txn) {
139 2112 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
140 int result;
141 2112 LogCvmfs(kLogCache, kLogDebug, "commit %s %s",
142 transaction->final_path.c_str(), transaction->tmp_path.c_str());
143
144 2112 result = Flush(transaction);
145 2112 close(transaction->fd);
146
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2099 times.
2112 if (result < 0) {
147 13 unlink(transaction->tmp_path.c_str());
148 13 transaction->~Transaction();
149 13 atomic_dec32(&no_inflight_txns_);
150 13 return result;
151 }
152
153 // To support debugging, move files into quarantine on file size mismatch
154
2/2
✓ Branch 0 taken 759 times.
✓ Branch 1 taken 1340 times.
2099 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 64 times.
✓ Branch 1 taken 695 times.
759 if ((transaction->expected_size != kSizeUnknown)
158
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
64 && (reports_correct_filesize_ || (transaction->size != 0))) {
159
1/3
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
64 LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
160 "size check failure for %s, expected %lu, got %lu",
161 128 transaction->id.ToString().c_str(), transaction->expected_size,
162 transaction->size);
163
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 CopyPath2Path(transaction->tmp_path,
164
2/4
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 64 times.
✗ Branch 6 not taken.
128 cache_path_ + "/quarantaine/" + transaction->id.ToString());
165 64 unlink(transaction->tmp_path.c_str());
166 64 transaction->~Transaction();
167 64 atomic_dec32(&no_inflight_txns_);
168 64 return -EIO;
169 }
170 }
171
172
2/2
✓ Branch 0 taken 2022 times.
✓ Branch 1 taken 13 times.
2035 if ((transaction->label.flags & kLabelPinned)
173
2/2
✓ Branch 0 taken 622 times.
✓ Branch 1 taken 1400 times.
2022 || (transaction->label.flags & kLabelCatalog)) {
174 1905 const bool retval = quota_mgr_->Pin(
175
1/2
✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
635 transaction->id, transaction->size, transaction->label.GetDescription(),
176 635 (transaction->label.flags & kLabelCatalog));
177
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 622 times.
635 if (!retval) {
178
1/2
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 LogCvmfs(kLogCache, kLogDebug, "commit failed: cannot pin %s",
179 26 transaction->id.ToString().c_str());
180 13 unlink(transaction->tmp_path.c_str());
181 13 transaction->~Transaction();
182 13 atomic_dec32(&no_inflight_txns_);
183 13 return -ENOSPC;
184 }
185 }
186
187 // Move the temporary file into its final location
188
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2009 times.
2022 if (alien_cache_) {
189 13 const int retval = chmod(transaction->tmp_path.c_str(), 0660);
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 assert(retval == 0);
191 }
192 2022 result = Rename(transaction->tmp_path.c_str(),
193 transaction->final_path.c_str());
194
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 1999 times.
2022 if (result < 0) {
195 23 LogCvmfs(kLogCache, kLogDebug, "commit failed: %s", strerror(errno));
196 23 unlink(transaction->tmp_path.c_str());
197
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 if ((transaction->label.flags & kLabelPinned)
198
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 10 times.
23 || (transaction->label.flags & kLabelCatalog)) {
199 13 quota_mgr_->Remove(transaction->id);
200 }
201 } else {
202 // Success, inform quota manager
203
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1986 times.
1999 if (transaction->label.flags & kLabelVolatile) {
204
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 quota_mgr_->InsertVolatile(transaction->id, transaction->size,
205 26 transaction->label.GetDescription());
206 1986 } else if (!transaction->label.IsCatalog()
207
6/6
✓ Branch 0 taken 1390 times.
✓ Branch 1 taken 596 times.
✓ Branch 3 taken 1377 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 1377 times.
✓ Branch 6 taken 609 times.
1986 && !transaction->label.IsPinned()) {
208
1/2
✓ Branch 1 taken 1377 times.
✗ Branch 2 not taken.
1377 quota_mgr_->Insert(transaction->id, transaction->size,
209 2754 transaction->label.GetDescription());
210 }
211 }
212 2022 transaction->~Transaction();
213 2022 atomic_dec32(&no_inflight_txns_);
214 2022 return result;
215 }
216
217 3189 bool PosixCacheManager::InitCacheDirectory(const string &cache_path) {
218
1/2
✓ Branch 1 taken 3189 times.
✗ Branch 2 not taken.
3189 const FileSystemInfo fs_info = GetFileSystemInfo(cache_path);
219
220
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 3169 times.
3189 if (fs_info.type == kFsTypeTmpfs) {
221 20 is_tmpfs_ = true;
222 }
223
224
2/2
✓ Branch 0 taken 485 times.
✓ Branch 1 taken 2704 times.
3189 if (alien_cache_) {
225
3/4
✓ Branch 1 taken 485 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 475 times.
485 if (!MakeCacheDirectories(cache_path, 0770)) {
226 10 return false;
227 }
228
1/2
✓ Branch 1 taken 475 times.
✗ Branch 2 not taken.
475 LogCvmfs(kLogCache, kLogDebug | kLogSyslog,
229 "Cache directory structure created.");
230
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 475 times.
475 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 475 default:
241 475 break;
242 }
243 } else {
244
3/4
✓ Branch 1 taken 2704 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 2694 times.
2704 if (!MakeCacheDirectories(cache_path, 0700))
245 10 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 3169 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3169 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✓ Branch 8 taken 3159 times.
3169 if (FileExists(cache_path + "/cvmfscatalog.cache")) {
250
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
251 "Not mounting on cvmfs 2.0.X cache");
252 10 return false;
253 }
254 3159 return true;
255 }
256
257 3189 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 3189 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3189 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3189 times.
✗ Branch 8 not taken.
3189 cache_path, alien_cache, do_refcount, cleanup_unused_first));
263
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3189 times.
3189 assert(cache_manager.IsValid());
264
265 3189 cache_manager->rename_workaround_ = rename_workaround;
266
267
1/2
✓ Branch 2 taken 3189 times.
✗ Branch 3 not taken.
3189 const bool result_ = cache_manager->InitCacheDirectory(cache_path);
268
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 3159 times.
3189 if (!result_) {
269 30 return NULL;
270 }
271
272 3159 return cache_manager.Release();
273 3189 }
274
275
276 2092 void PosixCacheManager::CtrlTxn(const Label &label,
277 const int flags,
278 void *txn) {
279 2092 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
280 2092 transaction->label = label;
281 2092 }
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 10 void *PosixCacheManager::DoSaveState() {
302
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (do_refcount_) {
303
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 SavedState *state = new SavedState();
304 10 state->fd_mgr = fd_mgr_->Clone();
305 10 return state;
306 }
307 char *c = reinterpret_cast<char *>(smalloc(1));
308 *c = kMagicNoRefcount;
309 return c;
310 }
311
312
313 10 int PosixCacheManager::DoRestoreState(void *data) {
314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 assert(data);
315
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (do_refcount_) {
316 10 SavedState *state = reinterpret_cast<SavedState *>(data);
317
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (state->magic_number == kMagicRefcount) {
318 10 LogCvmfs(kLogCache, kLogDebug,
319 "Restoring refcount cache manager from "
320 "refcounted posix cache manager");
321
322 10 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 10 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 10 bool PosixCacheManager::DoFreeState(void *data) {
347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 assert(data);
348 10 SavedState *state = reinterpret_cast<SavedState *>(data);
349
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (state->magic_number == kMagicRefcount) {
350
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 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 10 return true;
357 }
358
359
360 80 int PosixCacheManager::Dup(int fd) {
361
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 const int new_fd = do_refcount_ ? fd_mgr_->Dup(fd) : dup(fd);
362
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 40 times.
80 if (new_fd < 0)
363 40 return -errno;
364 40 return new_fd;
365 }
366
367
368 5082 int PosixCacheManager::Flush(Transaction *transaction) {
369
2/2
✓ Branch 0 taken 1214 times.
✓ Branch 1 taken 3868 times.
5082 if (transaction->buf_pos == 0)
370 1214 return 0;
371 7736 const int written = write(transaction->fd, transaction->buffer,
372 3868 transaction->buf_pos);
373
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 3835 times.
3868 if (written < 0)
374 33 return -errno;
375
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3835 times.
3835 if (static_cast<unsigned>(written) != transaction->buf_pos) {
376 transaction->buf_pos -= written;
377 return -EIO;
378 }
379 3835 transaction->buf_pos = 0;
380 3835 return 0;
381 }
382
383
384 5520 inline string PosixCacheManager::GetPathInCache(const shash::Any &id) {
385
2/4
✓ Branch 2 taken 5520 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5520 times.
✗ Branch 6 not taken.
11040 return cache_path_ + "/" + id.MakePathWithoutSuffix();
386 }
387
388
389 1364 int64_t PosixCacheManager::GetSize(int fd) {
390 platform_stat64 info;
391 1364 const int retval = platform_fstat(fd, &info);
392
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1351 times.
1364 if (retval != 0)
393 13 return -errno;
394 1351 return info.st_size;
395 }
396
397
398 3107 int PosixCacheManager::Open(const LabeledObject &object) {
399
1/2
✓ Branch 1 taken 3107 times.
✗ Branch 2 not taken.
3107 const string path = GetPathInCache(object.id);
400 int result;
401
1/2
✓ Branch 0 taken 3107 times.
✗ Branch 1 not taken.
3107 if (do_refcount_) {
402
1/2
✓ Branch 2 taken 3107 times.
✗ Branch 3 not taken.
3107 result = fd_mgr_->Open(object.id, path);
403 } else {
404 result = open(path.c_str(), O_RDONLY);
405 }
406
2/2
✓ Branch 0 taken 817 times.
✓ Branch 1 taken 2290 times.
3107 if (result >= 0) {
407
1/2
✓ Branch 2 taken 817 times.
✗ Branch 3 not taken.
817 LogCvmfs(kLogCache, kLogDebug, "hit %s", path.c_str());
408 // platform_disable_kcache(result);
409
1/2
✓ Branch 1 taken 817 times.
✗ Branch 2 not taken.
817 quota_mgr_->Touch(object.id);
410 } else {
411 2290 result = -errno;
412
1/2
✓ Branch 2 taken 2290 times.
✗ Branch 3 not taken.
2290 LogCvmfs(kLogCache, kLogDebug, "miss %s (%d)", path.c_str(), result);
413 }
414 3107 return result;
415 3107 }
416
417
418 843 int PosixCacheManager::OpenFromTxn(void *txn) {
419 843 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
420 843 const int retval = Flush(transaction);
421
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 833 times.
843 if (retval < 0)
422 10 return retval;
423 int fd_rdonly;
424
425
1/2
✓ Branch 0 taken 833 times.
✗ Branch 1 not taken.
833 if (do_refcount_) {
426
2/4
✓ Branch 4 taken 833 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 833 times.
✗ Branch 8 not taken.
833 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 10 times.
✓ Branch 1 taken 823 times.
833 if (fd_rdonly == -1)
431 10 return -errno;
432 823 return fd_rdonly;
433 }
434
435
436 12455 int64_t PosixCacheManager::Pread(int fd,
437 void *buf,
438 uint64_t size,
439 uint64_t offset) {
440 int64_t result;
441 do {
442 12455 errno = 0;
443 12455 result = pread(fd, buf, size, offset);
444
3/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12429 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
12455 } while ((result == -1) && (errno == EINTR));
445
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12429 times.
12455 if (result < 0)
446 26 return -errno;
447 12429 return result;
448 }
449
450
451 2082 int PosixCacheManager::Rename(const char *oldpath, const char *newpath) {
452 int result;
453
2/2
✓ Branch 0 taken 2052 times.
✓ Branch 1 taken 30 times.
2082 if (rename_workaround_ != kRenameLink) {
454 2052 result = rename(oldpath, newpath);
455
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 2019 times.
2052 if (result < 0)
456 33 return -errno;
457 2019 return 0;
458 }
459
460 30 result = link(oldpath, newpath);
461
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 10 times.
30 if (result < 0) {
462
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
20 if (errno == EEXIST) {
463 10 LogCvmfs(kLogCache, kLogDebug, "%s already existed, ignoring", newpath);
464 } else {
465 10 return -errno;
466 }
467 }
468 20 result = unlink(oldpath);
469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (result < 0)
470 return -errno;
471 20 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 847 int PosixCacheManager::Readahead(int fd) {
482 unsigned char *buf[4096];
483 int nbytes;
484 847 uint64_t pos = 0;
485
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 847 times.
847 if (is_tmpfs()) {
486 return 0;
487 }
488 do {
489
1/2
✓ Branch 1 taken 3987 times.
✗ Branch 2 not taken.
3987 nbytes = Pread(fd, buf, 4096, pos);
490 3987 pos += nbytes;
491
2/2
✓ Branch 0 taken 3140 times.
✓ Branch 1 taken 847 times.
3987 } while (nbytes == 4096);
492
1/2
✓ Branch 1 taken 847 times.
✗ Branch 2 not taken.
847 LogCvmfs(kLogCache, kLogDebug, "read-ahead %d, %" PRIu64, fd, pos);
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 847 times.
847 if (nbytes < 0)
494 return nbytes;
495 847 return 0;
496 }
497
498
499 175 int PosixCacheManager::Reset(void *txn) {
500 175 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
501 175 transaction->buf_pos = 0;
502 175 transaction->size = 0;
503 175 int retval = lseek(transaction->fd, 0, SEEK_SET);
504
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 165 times.
175 if (retval < 0)
505 10 return -errno;
506 165 retval = ftruncate(transaction->fd, 0);
507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 165 times.
165 if (retval < 0)
508 return -errno;
509 165 return 0;
510 }
511
512
513 2433 int PosixCacheManager::StartTxn(const shash::Any &id,
514 uint64_t size,
515 void *txn) {
516 2433 atomic_inc32(&no_inflight_txns_);
517
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2423 times.
2433 if (cache_mode_ == kCacheReadOnly) {
518 10 atomic_dec32(&no_inflight_txns_);
519 10 return -EROFS;
520 }
521
522
2/2
✓ Branch 0 taken 1573 times.
✓ Branch 1 taken 850 times.
2423 if (size != kSizeUnknown) {
523
3/4
✓ Branch 1 taken 1573 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 1563 times.
1573 if (size > quota_mgr_->GetMaxFileSize()) {
524
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 LogCvmfs(kLogCache, kLogDebug,
525 "file too big for lru cache (%" PRIu64 " "
526 "requested but only %" PRIu64 " bytes free)",
527
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 size, quota_mgr_->GetMaxFileSize());
528 10 atomic_dec32(&no_inflight_txns_);
529 10 return -ENOSPC;
530 }
531
532 // For large files, ensure enough free cache space before writing the chunk
533
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1553 times.
1563 if (size > kBigFile) {
534
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 const uint64_t cache_size = quota_mgr_->GetSize();
535
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 const uint64_t cache_capacity = quota_mgr_->GetCapacity();
536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 assert(cache_capacity >= size);
537
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if ((cache_size + size) > cache_capacity) {
538 20 const uint64_t leave_size = std::min(cache_capacity / 2,
539 10 cache_capacity - size);
540
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 quota_mgr_->Cleanup(leave_size);
541 }
542 }
543 }
544
545
1/2
✓ Branch 1 taken 2413 times.
✗ Branch 2 not taken.
2413 string path_in_cache = GetPathInCache(id);
546
1/2
✓ Branch 2 taken 2413 times.
✗ Branch 3 not taken.
2413 Transaction *transaction = new (txn) Transaction(id, path_in_cache);
547
548 2413 char *template_path = NULL;
549 2413 unsigned temp_path_len = 0;
550
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 2393 times.
2413 if (rename_workaround_ == kRenameSamedir) {
551 20 temp_path_len = path_in_cache.length() + 6;
552 20 template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1));
553 20 memcpy(template_path, path_in_cache.data(), path_in_cache.length());
554 20 memset(template_path + path_in_cache.length(), 'X', 6);
555 } else {
556 2393 temp_path_len = txn_template_path_.length();
557 2393 template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1));
558
1/2
✓ Branch 1 taken 2393 times.
✗ Branch 2 not taken.
2393 memcpy(template_path, &txn_template_path_[0], temp_path_len);
559 }
560 2413 template_path[temp_path_len] = '\0';
561
562
1/2
✓ Branch 1 taken 2413 times.
✗ Branch 2 not taken.
2413 transaction->fd = mkstemp(template_path);
563
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 2376 times.
2413 if (transaction->fd == -1) {
564 37 transaction->~Transaction();
565 37 atomic_dec32(&no_inflight_txns_);
566 37 return -errno;
567 }
568
569
1/2
✓ Branch 1 taken 2376 times.
✗ Branch 2 not taken.
2376 LogCvmfs(kLogCache, kLogDebug, "start transaction on %s has result %d",
570 template_path, transaction->fd);
571
1/2
✓ Branch 1 taken 2376 times.
✗ Branch 2 not taken.
2376 transaction->tmp_path = template_path;
572 2376 transaction->expected_size = size;
573 2376 return transaction->fd;
574 2413 }
575
576
577 479 manifest::Breadcrumb PosixCacheManager::LoadBreadcrumb(
578 const std::string &fqrn) {
579 479 return manifest::Manifest::ReadBreadcrumb(fqrn, cache_path_);
580 }
581
582
583 188 bool PosixCacheManager::StoreBreadcrumb(const manifest::Manifest &manifest) {
584 188 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 40 void PosixCacheManager::TearDown2ReadOnly() {
595 40 cache_mode_ = kCacheReadOnly;
596
2/2
✓ Branch 1 taken 140 times.
✓ Branch 2 taken 30 times.
170 while (atomic_read32(&no_inflight_txns_) != 0)
597 140 SafeSleepMs(50);
598
599 30 QuotaManager *old_manager = quota_mgr_;
600
1/2
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
30 quota_mgr_ = new NoopQuotaManager();
601
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 delete old_manager;
602 30 }
603
604
605 2740 int64_t PosixCacheManager::Write(const void *buf, uint64_t size, void *txn) {
606 2740 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
607
608
2/2
✓ Branch 0 taken 1435 times.
✓ Branch 1 taken 1305 times.
2740 if (transaction->expected_size != kSizeUnknown) {
609
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1425 times.
1435 if (transaction->size + size > transaction->expected_size) {
610 10 LogCvmfs(kLogCache, kLogDebug,
611 "Transaction size (%" PRIu64 ") > expected size (%" PRIu64 ")",
612 10 transaction->size + size, transaction->expected_size);
613 10 return -EFBIG;
614 }
615 }
616
617 2730 uint64_t written = 0;
618 2730 const unsigned char *read_pos = reinterpret_cast<const unsigned char *>(buf);
619
2/2
✓ Branch 0 taken 3952 times.
✓ Branch 1 taken 2720 times.
6672 while (written < size) {
620
2/2
✓ Branch 0 taken 2127 times.
✓ Branch 1 taken 1825 times.
3952 if (transaction->buf_pos == sizeof(transaction->buffer)) {
621
1/2
✓ Branch 1 taken 2127 times.
✗ Branch 2 not taken.
2127 const int retval = Flush(transaction);
622
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2117 times.
2127 if (retval != 0) {
623 10 transaction->size += written;
624 10 return retval;
625 }
626 }
627 3942 const uint64_t remaining = size - written;
628 3942 const uint64_t space_in_buffer = sizeof(transaction->buffer)
629 3942 - transaction->buf_pos;
630 3942 const uint64_t batch_size = std::min(remaining, space_in_buffer);
631 3942 memcpy(transaction->buffer + transaction->buf_pos, read_pos, batch_size);
632 3942 transaction->buf_pos += batch_size;
633 3942 written += batch_size;
634 3942 read_pos += batch_size;
635 }
636 2720 transaction->size += written;
637 2720 return written;
638 }
639
640