GCC Code Coverage Report


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