GCC Code Coverage Report


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