GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_posix.cc
Date: 2026-08-09 02:40:25
Exec Total Coverage
Lines: 297 339 87.6%
Branches: 178 283 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/mutex.h"
53 #include "util/platform.h"
54 #include "util/posix.h"
55 #include "util/smalloc.h"
56
57 using namespace std; // NOLINT
58
59 namespace {
60
61 /**
62 * A CallGuard object can be placed at the beginning of a function. It counts
63 * the number of so-annotated functions that are in flight. The Drainout() call
64 * will wait until all functions that have been called so far are finished.
65 *
66 * The class is used in order to wait for remaining calls when switching into
67 * the read-only cache mode.
68 */
69 class CallGuard {
70 public:
71 CallGuard() {
72 const int32_t global_drainout = atomic_read32(&global_drainout_);
73 drainout_ = (global_drainout != 0);
74 if (!drainout_)
75 atomic_inc32(&num_inflight_calls_);
76 }
77 ~CallGuard() {
78 if (!drainout_)
79 atomic_dec32(&num_inflight_calls_);
80 }
81 static void Drainout() {
82 atomic_cas32(&global_drainout_, 0, 1);
83 while (atomic_read32(&num_inflight_calls_) != 0)
84 SafeSleepMs(50);
85 }
86
87 private:
88 bool drainout_;
89 static atomic_int32 global_drainout_;
90 static atomic_int32 num_inflight_calls_;
91 };
92 atomic_int32 CallGuard::num_inflight_calls_ = 0;
93 atomic_int32 CallGuard::global_drainout_ = 0;
94
95 } // anonymous namespace
96
97
98 //------------------------------------------------------------------------------
99
100
101 const uint64_t PosixCacheManager::kBigFile = 25 * 1024 * 1024; // 25M
102
103
104 815 int PosixCacheManager::AbortTxn(void *txn) {
105 815 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
106 815 LogCvmfs(kLogCache, kLogDebug, "abort %s", transaction->tmp_path.c_str());
107 815 close(transaction->fd);
108 815 const int result = unlink(transaction->tmp_path.c_str());
109 815 transaction->~Transaction();
110 815 atomic_dec32(&no_inflight_txns_);
111
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 727 times.
815 if (result == -1)
112 88 return -errno;
113 727 return 0;
114 }
115
116
117 /**
118 * This should only be used to replace the default NoopQuotaManager by a
119 * PosixQuotaManager. The cache manager takes the ownership of the passed
120 * quota manager.
121 */
122 1378 bool PosixCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) {
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1378 times.
1378 if (quota_mgr == NULL)
124 return false;
125
1/2
✓ Branch 0 taken 1378 times.
✗ Branch 1 not taken.
1378 delete quota_mgr_;
126 1378 quota_mgr_ = quota_mgr;
127 1378 return true;
128 }
129
130
131 2824 int PosixCacheManager::Close(int fd) {
132
1/2
✓ Branch 0 taken 2824 times.
✗ Branch 1 not taken.
2824 const int retval = do_refcount_ ? fd_mgr_->Close(fd) : close(fd);
133
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 2780 times.
2824 if (retval != 0)
134 44 return -errno;
135 2780 return 0;
136 }
137
138
139 5562 int PosixCacheManager::CommitTxn(void *txn) {
140 5562 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
141 int result;
142 5562 LogCvmfs(kLogCache, kLogDebug, "commit %s %s",
143 transaction->final_path.c_str(), transaction->tmp_path.c_str());
144
145 5562 result = Flush(transaction);
146 5562 close(transaction->fd);
147
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 5518 times.
5562 if (result < 0) {
148 44 unlink(transaction->tmp_path.c_str());
149 44 transaction->~Transaction();
150 44 atomic_dec32(&no_inflight_txns_);
151 44 return result;
152 }
153
154 // To support debugging, move files into quarantine on file size mismatch
155
2/2
✓ Branch 0 taken 924 times.
✓ Branch 1 taken 4594 times.
5518 if (transaction->size != transaction->expected_size) {
156 // Allow size to be zero if alien cache, because hadoop-fuse-dfs returns
157 // size zero for a while
158
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 784 times.
924 if ((transaction->expected_size != kSizeUnknown)
159
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 140 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
140 && (reports_correct_filesize_ || (transaction->size != 0))) {
160
1/3
✓ Branch 2 taken 140 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
140 LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
161 "size check failure for %s, expected %lu, got %lu",
162 280 transaction->id.ToString().c_str(), transaction->expected_size,
163 transaction->size);
164
1/2
✓ Branch 1 taken 140 times.
✗ Branch 2 not taken.
140 CopyPath2Path(transaction->tmp_path,
165
2/4
✓ Branch 2 taken 140 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 140 times.
✗ Branch 6 not taken.
280 cache_path_ + "/quarantaine/" + transaction->id.ToString());
166 140 unlink(transaction->tmp_path.c_str());
167 140 transaction->~Transaction();
168 140 atomic_dec32(&no_inflight_txns_);
169 140 return -EIO;
170 }
171 }
172
173
2/2
✓ Branch 0 taken 5334 times.
✓ Branch 1 taken 44 times.
5378 if ((transaction->label.flags & kLabelPinned)
174
2/2
✓ Branch 0 taken 728 times.
✓ Branch 1 taken 4606 times.
5334 || (transaction->label.flags & kLabelCatalog)) {
175 2316 const bool retval = quota_mgr_->Pin(
176
1/2
✓ Branch 1 taken 772 times.
✗ Branch 2 not taken.
772 transaction->id, transaction->size, transaction->label.GetDescription(),
177 772 (transaction->label.flags & kLabelCatalog));
178
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 728 times.
772 if (!retval) {
179
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 LogCvmfs(kLogCache, kLogDebug, "commit failed: cannot pin %s",
180 88 transaction->id.ToString().c_str());
181 44 unlink(transaction->tmp_path.c_str());
182 44 transaction->~Transaction();
183 44 atomic_dec32(&no_inflight_txns_);
184 44 return -ENOSPC;
185 }
186 }
187
188 // Move the temporary file into its final location
189
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 5246 times.
5334 if (alien_cache_) {
190 88 const int retval = chmod(transaction->tmp_path.c_str(), 0660);
191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 assert(retval == 0);
192 }
193 5334 result = Rename(transaction->tmp_path.c_str(),
194 transaction->final_path.c_str());
195
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 5246 times.
5334 if (result < 0) {
196 88 LogCvmfs(kLogCache, kLogDebug, "commit failed: %s", strerror(errno));
197 88 unlink(transaction->tmp_path.c_str());
198
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 if ((transaction->label.flags & kLabelPinned)
199
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 44 times.
88 || (transaction->label.flags & kLabelCatalog)) {
200 44 quota_mgr_->Remove(transaction->id);
201 }
202 } else {
203 // Success, inform quota manager
204
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 5202 times.
5246 if (transaction->label.flags & kLabelVolatile) {
205
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 quota_mgr_->InsertVolatile(transaction->id, transaction->size,
206 88 transaction->label.GetDescription());
207 5202 } else if (!transaction->label.IsCatalog()
208
6/6
✓ Branch 0 taken 4562 times.
✓ Branch 1 taken 640 times.
✓ Branch 3 taken 4518 times.
✓ Branch 4 taken 44 times.
✓ Branch 5 taken 4518 times.
✓ Branch 6 taken 684 times.
5202 && !transaction->label.IsPinned()) {
209
1/2
✓ Branch 1 taken 4518 times.
✗ Branch 2 not taken.
4518 quota_mgr_->Insert(transaction->id, transaction->size,
210 9036 transaction->label.GetDescription());
211 }
212 }
213 5334 transaction->~Transaction();
214 5334 atomic_dec32(&no_inflight_txns_);
215 5334 return result;
216 }
217
218 5680 bool PosixCacheManager::InitCacheDirectory(const string &cache_path) {
219 // For an alien cache the directory skeleton is created lazily on the first
220 // write (see EnsureCacheDirectories), so that merely trying to mount a bogus
221 // fqrn -- e.g. a stray access to /cvmfs/<typo> -- does not leave empty
222 // directories behind in a per-fqrn alien cache (see #4217). A regular cache
223 // coincides with the workspace and backs the quota manager, which scans the
224 // 00..ff directories at start-up, so it is created eagerly.
225
2/2
✓ Branch 0 taken 4298 times.
✓ Branch 1 taken 1382 times.
5680 if (!alien_cache_) {
226
2/2
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 4254 times.
4298 if (!EnsureCacheDirectories())
227 44 return false;
228 }
229
230 // TODO(jblomer): we might not need to look anymore for cvmfs 2.0 relicts
231
3/5
✓ Branch 2 taken 5636 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 44 times.
✓ Branch 6 taken 5592 times.
5636 if (FileExists(cache_path + "/cvmfscatalog.cache")) {
232 44 LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
233 "Not mounting on cvmfs 2.0.X cache");
234 44 return false;
235 }
236 5592 return true;
237 }
238
239
240 /**
241 * Idempotently create the on-disk cache skeleton and detect the underlying
242 * file system. For alien caches this is deferred until the first cache write
243 * so that the skeleton is only materialized once the repository has actually
244 * been validated and content is about to be stored (see #4217).
245 */
246 10855 bool PosixCacheManager::EnsureCacheDirectories() {
247
2/2
✓ Branch 1 taken 6469 times.
✓ Branch 2 taken 4386 times.
10855 if (atomic_read32(&cache_dirs_created_))
248 6469 return true;
249
250 4386 const MutexLockGuard guard(lock_cache_dirs_);
251
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4386 times.
4386 if (atomic_read32(&cache_dirs_created_))
252 return true;
253
254
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 4298 times.
4386 const mode_t mode = alien_cache_ ? 0770 : 0700;
255
256 // Create the base directory first so that the file system type can be
257 // detected reliably: statfs() on a not-yet-existing path returns nothing.
258
3/4
✓ Branch 1 taken 4386 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 4342 times.
4386 if (!MkdirDeep(cache_path_, mode, false)) {
259
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
260 "Failed to create cache directory %s", cache_path_.c_str());
261 44 return false;
262 }
263
264
1/2
✓ Branch 1 taken 4342 times.
✗ Branch 2 not taken.
4342 const FileSystemInfo fs_info = GetFileSystemInfo(cache_path_);
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4342 times.
4342 if (fs_info.type == kFsTypeTmpfs)
266 is_tmpfs_ = true;
267
268
2/4
✓ Branch 1 taken 4342 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4342 times.
4342 if (!MakeCacheDirectories(cache_path_, mode))
269 return false;
270
271
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 4254 times.
4342 if (alien_cache_) {
272 // Sentinel file for future use (see FileSystem::SetupPosixCacheMgr). For
273 // an alien cache the skeleton -- and hence this file -- is created here
274 // lazily rather than at mount time; a read-only alien cache may reject it.
275
2/4
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 88 times.
✗ Branch 5 not taken.
88 CreateFile(cache_path_ + "/.cvmfscache", 0600, true /* ignore_failure */);
276
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 LogCvmfs(kLogCache, kLogDebug | kLogSyslog,
277 "Cache directory structure created.");
278
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
88 switch (fs_info.type) {
279 case kFsTypeNFS:
280 rename_workaround_ = kRenameLink;
281 LogCvmfs(kLogCache, kLogDebug | kLogSyslog, "Alien cache is on NFS.");
282 break;
283 case kFsTypeBeeGFS:
284 rename_workaround_ = kRenameSamedir;
285 LogCvmfs(kLogCache, kLogDebug | kLogSyslog,
286 "Alien cache is on BeeGFS.");
287 break;
288 88 default:
289 88 break;
290 }
291 }
292
293 4342 atomic_write32(&cache_dirs_created_, 1);
294 4342 return true;
295 4386 }
296
297 5680 PosixCacheManager *PosixCacheManager::Create(
298 const string &cache_path, const bool alien_cache,
299 const RenameWorkarounds rename_workaround, const bool do_refcount,
300 const bool cleanup_unused_first) {
301 UniquePtr<PosixCacheManager> cache_manager(new PosixCacheManager(
302
3/6
✓ Branch 1 taken 5680 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5680 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5680 times.
✗ Branch 8 not taken.
5680 cache_path, alien_cache, do_refcount, cleanup_unused_first));
303
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5680 times.
5680 assert(cache_manager.IsValid());
304
305 5680 cache_manager->rename_workaround_ = rename_workaround;
306
307
1/2
✓ Branch 2 taken 5680 times.
✗ Branch 3 not taken.
5680 const bool result_ = cache_manager->InitCacheDirectory(cache_path);
308
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 5592 times.
5680 if (!result_) {
309 88 return NULL;
310 }
311
312 5592 return cache_manager.Release();
313 5680 }
314
315
316 5192 void PosixCacheManager::CtrlTxn(const Label &label,
317 const int flags,
318 void *txn) {
319 5192 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
320 5192 transaction->label = label;
321 5192 }
322
323
324 string PosixCacheManager::Describe() {
325 string msg;
326 if (do_refcount_) {
327 msg = "Refcounting Posix cache manager"
328 "(cache directory: "
329 + cache_path_ + ")\n";
330 } else {
331 msg = "Posix cache manager (cache directory: " + cache_path_ + ")\n";
332 }
333 return msg;
334 }
335
336
337 /**
338 * If not refcounting, nothing to do, the kernel keeps the state
339 * of open file descriptors. Return a dummy memory location.
340 */
341 41 void *PosixCacheManager::DoSaveState() {
342
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 if (do_refcount_) {
343
1/2
✓ Branch 2 taken 41 times.
✗ Branch 3 not taken.
41 SavedState *state = new SavedState();
344 41 state->fd_mgr = fd_mgr_->Clone();
345 41 return state;
346 }
347 char *c = reinterpret_cast<char *>(smalloc(1));
348 *c = kMagicNoRefcount;
349 return c;
350 }
351
352
353 41 int PosixCacheManager::DoRestoreState(void *data) {
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
41 assert(data);
355
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 if (do_refcount_) {
356 41 SavedState *state = reinterpret_cast<SavedState *>(data);
357
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 if (state->magic_number == kMagicRefcount) {
358 41 LogCvmfs(kLogCache, kLogDebug,
359 "Restoring refcount cache manager from "
360 "refcounted posix cache manager");
361
362 41 fd_mgr_->AssignFrom(state->fd_mgr.weak_ref());
363 } else {
364 LogCvmfs(kLogCache, kLogDebug,
365 "Restoring refcount cache manager from "
366 "non-refcounted posix cache manager");
367 }
368 41 return -1;
369 }
370
371 char *c = reinterpret_cast<char *>(data);
372 assert(*c == kMagicNoRefcount || *c == kMagicRefcount);
373 if (*c == kMagicRefcount) {
374 SavedState *state = reinterpret_cast<SavedState *>(data);
375 LogCvmfs(kLogCache, kLogDebug,
376 "Restoring non-refcount cache manager from "
377 "refcounted posix cache manager - this "
378 " is not possible, keep refcounting.");
379 fd_mgr_->AssignFrom(state->fd_mgr.weak_ref());
380 do_refcount_ = true;
381 }
382 return -1;
383 }
384
385
386 41 bool PosixCacheManager::DoFreeState(void *data) {
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
41 assert(data);
388 41 SavedState *state = reinterpret_cast<SavedState *>(data);
389
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 if (state->magic_number == kMagicRefcount) {
390
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 delete state;
391 } else {
392 // If not refcounted, the state is the dummy SavedState
393 // of the regular posix cache manager
394 free(data);
395 }
396 41 return true;
397 }
398
399
400 184 int PosixCacheManager::Dup(int fd) {
401
1/2
✓ Branch 0 taken 184 times.
✗ Branch 1 not taken.
184 const int new_fd = do_refcount_ ? fd_mgr_->Dup(fd) : dup(fd);
402
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 92 times.
184 if (new_fd < 0)
403 92 return -errno;
404 92 return new_fd;
405 }
406
407
408 9710 int PosixCacheManager::Flush(Transaction *transaction) {
409
2/2
✓ Branch 0 taken 2558 times.
✓ Branch 1 taken 7152 times.
9710 if (transaction->buf_pos == 0)
410 2558 return 0;
411 14304 const int written = write(transaction->fd, transaction->buffer,
412 7152 transaction->buf_pos);
413
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 7020 times.
7152 if (written < 0)
414 132 return -errno;
415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7020 times.
7020 if (static_cast<unsigned>(written) != transaction->buf_pos) {
416 transaction->buf_pos -= written;
417 return -EIO;
418 }
419 7020 transaction->buf_pos = 0;
420 7020 return 0;
421 }
422
423
424 11342 inline string PosixCacheManager::GetPathInCache(const shash::Any &id) {
425
2/4
✓ Branch 2 taken 11342 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11342 times.
✗ Branch 6 not taken.
22684 return cache_path_ + "/" + id.MakePathWithoutSuffix();
426 }
427
428
429 2093 int64_t PosixCacheManager::GetSize(int fd) {
430 platform_stat64 info;
431 2093 const int retval = platform_fstat(fd, &info);
432
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 2052 times.
2093 if (retval != 0)
433 41 return -errno;
434 2052 return info.st_size;
435 }
436
437
438 4873 int PosixCacheManager::Open(const LabeledObject &object) {
439
1/2
✓ Branch 1 taken 4873 times.
✗ Branch 2 not taken.
4873 const string path = GetPathInCache(object.id);
440 int result;
441
1/2
✓ Branch 0 taken 4873 times.
✗ Branch 1 not taken.
4873 if (do_refcount_) {
442
1/2
✓ Branch 2 taken 4873 times.
✗ Branch 3 not taken.
4873 result = fd_mgr_->Open(object.id, path);
443 } else {
444 result = open(path.c_str(), O_RDONLY);
445 }
446
2/2
✓ Branch 0 taken 1673 times.
✓ Branch 1 taken 3200 times.
4873 if (result >= 0) {
447
1/2
✓ Branch 2 taken 1673 times.
✗ Branch 3 not taken.
1673 LogCvmfs(kLogCache, kLogDebug, "hit %s", path.c_str());
448 // platform_disable_kcache(result);
449
1/2
✓ Branch 1 taken 1673 times.
✗ Branch 2 not taken.
1673 quota_mgr_->Touch(object.id);
450 } else {
451 3200 result = -errno;
452
1/2
✓ Branch 2 taken 3200 times.
✗ Branch 3 not taken.
3200 LogCvmfs(kLogCache, kLogDebug, "miss %s (%d)", path.c_str(), result);
453 }
454 4873 return result;
455 4873 }
456
457
458 1152 int PosixCacheManager::OpenFromTxn(void *txn) {
459 1152 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
460 1152 const int retval = Flush(transaction);
461
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 1108 times.
1152 if (retval < 0)
462 44 return retval;
463 int fd_rdonly;
464
465
1/2
✓ Branch 0 taken 1108 times.
✗ Branch 1 not taken.
1108 if (do_refcount_) {
466
2/4
✓ Branch 4 taken 1108 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1108 times.
✗ Branch 8 not taken.
1108 fd_rdonly = fd_mgr_->Open(transaction->id, transaction->tmp_path.c_str());
467 } else {
468 fd_rdonly = open(transaction->tmp_path.c_str(), O_RDONLY);
469 }
470
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 1064 times.
1108 if (fd_rdonly == -1)
471 44 return -errno;
472 1064 return fd_rdonly;
473 }
474
475
476 14230 int64_t PosixCacheManager::Pread(int fd,
477 void *buf,
478 uint64_t size,
479 uint64_t offset) {
480 int64_t result;
481 do {
482 14230 errno = 0;
483 14230 result = pread(fd, buf, size, offset);
484
3/4
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 14142 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 88 times.
14230 } while ((result == -1) && (errno == EINTR));
485
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 14142 times.
14230 if (result < 0)
486 88 return -errno;
487 14142 return result;
488 }
489
490
491 5598 int PosixCacheManager::Rename(const char *oldpath, const char *newpath) {
492 int result;
493
2/2
✓ Branch 0 taken 5466 times.
✓ Branch 1 taken 132 times.
5598 if (rename_workaround_ != kRenameLink) {
494 5466 result = rename(oldpath, newpath);
495
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 5334 times.
5466 if (result < 0)
496 132 return -errno;
497 5334 return 0;
498 }
499
500 132 result = link(oldpath, newpath);
501
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 44 times.
132 if (result < 0) {
502
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 44 times.
88 if (errno == EEXIST) {
503 44 LogCvmfs(kLogCache, kLogDebug, "%s already existed, ignoring", newpath);
504 } else {
505 44 return -errno;
506 }
507 }
508 88 result = unlink(oldpath);
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 if (result < 0)
510 return -errno;
511 88 return 0;
512 }
513
514
515 /**
516 * Used by the sqlite vfs in order to preload file catalogs into the file system
517 * buffers.
518 *
519 * No-op if the fd is to a file that is on a tmpfs, and so already in page cache
520 */
521 975 int PosixCacheManager::Readahead(int fd) {
522 unsigned char *buf[4096];
523 int nbytes;
524 975 uint64_t pos = 0;
525
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 975 times.
975 if (is_tmpfs()) {
526 return 0;
527 }
528 do {
529
1/2
✓ Branch 1 taken 4523 times.
✗ Branch 2 not taken.
4523 nbytes = Pread(fd, buf, 4096, pos);
530 4523 pos += nbytes;
531
2/2
✓ Branch 0 taken 3548 times.
✓ Branch 1 taken 975 times.
4523 } while (nbytes == 4096);
532
1/2
✓ Branch 1 taken 975 times.
✗ Branch 2 not taken.
975 LogCvmfs(kLogCache, kLogDebug, "read-ahead %d, %" PRIu64, fd, pos);
533
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 975 times.
975 if (nbytes < 0)
534 return nbytes;
535 975 return 0;
536 }
537
538
539 379 int PosixCacheManager::Reset(void *txn) {
540 379 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
541 379 transaction->buf_pos = 0;
542 379 transaction->size = 0;
543 379 int retval = lseek(transaction->fd, 0, SEEK_SET);
544
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 335 times.
379 if (retval < 0)
545 44 return -errno;
546 335 retval = ftruncate(transaction->fd, 0);
547
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 335 times.
335 if (retval < 0)
548 return -errno;
549 335 return 0;
550 }
551
552
553 6557 int PosixCacheManager::StartTxn(const shash::Any &id,
554 uint64_t size,
555 void *txn) {
556 // Materialize the cache skeleton on first use. For alien caches this is
557 // deferred from mount time to here, so that a bogus fqrn never creates any
558 // directories (see #4217); for regular caches this is a lock-free no-op.
559
2/4
✓ Branch 1 taken 6557 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6557 times.
6557 if (!EnsureCacheDirectories())
560 return -EIO;
561
562 6557 atomic_inc32(&no_inflight_txns_);
563
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 6513 times.
6557 if (cache_mode_ == kCacheReadOnly) {
564 44 atomic_dec32(&no_inflight_txns_);
565 44 return -EROFS;
566 }
567
568
2/2
✓ Branch 0 taken 5438 times.
✓ Branch 1 taken 1075 times.
6513 if (size != kSizeUnknown) {
569
3/4
✓ Branch 1 taken 5438 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 5394 times.
5438 if (size > quota_mgr_->GetMaxFileSize()) {
570
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 LogCvmfs(kLogCache, kLogDebug,
571 "file too big for lru cache (%" PRIu64 " "
572 "requested but only %" PRIu64 " bytes free)",
573
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 size, quota_mgr_->GetMaxFileSize());
574 44 atomic_dec32(&no_inflight_txns_);
575 44 return -ENOSPC;
576 }
577
578 // For large files, ensure enough free cache space before writing the chunk
579
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 5350 times.
5394 if (size > kBigFile) {
580
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 const uint64_t cache_size = quota_mgr_->GetSize();
581
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 const uint64_t cache_capacity = quota_mgr_->GetCapacity();
582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 assert(cache_capacity >= size);
583
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 if ((cache_size + size) > cache_capacity) {
584 88 const uint64_t leave_size = std::min(cache_capacity / 2,
585 44 cache_capacity - size);
586
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 quota_mgr_->Cleanup(leave_size);
587 }
588 }
589 }
590
591
1/2
✓ Branch 1 taken 6469 times.
✗ Branch 2 not taken.
6469 string path_in_cache = GetPathInCache(id);
592
1/2
✓ Branch 2 taken 6469 times.
✗ Branch 3 not taken.
6469 Transaction *transaction = new (txn) Transaction(id, path_in_cache);
593
594 6469 char *template_path = NULL;
595 6469 unsigned temp_path_len = 0;
596
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 6381 times.
6469 if (rename_workaround_ == kRenameSamedir) {
597 88 temp_path_len = path_in_cache.length() + 6;
598 88 template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1));
599 88 memcpy(template_path, path_in_cache.data(), path_in_cache.length());
600 88 memset(template_path + path_in_cache.length(), 'X', 6);
601 } else {
602 6381 temp_path_len = txn_template_path_.length();
603 6381 template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1));
604
1/2
✓ Branch 1 taken 6381 times.
✗ Branch 2 not taken.
6381 memcpy(template_path, &txn_template_path_[0], temp_path_len);
605 }
606 6469 template_path[temp_path_len] = '\0';
607
608
1/2
✓ Branch 1 taken 6469 times.
✗ Branch 2 not taken.
6469 transaction->fd = mkstemp(template_path);
609
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 6377 times.
6469 if (transaction->fd == -1) {
610 92 transaction->~Transaction();
611 92 atomic_dec32(&no_inflight_txns_);
612 92 return -errno;
613 }
614
615
1/2
✓ Branch 1 taken 6377 times.
✗ Branch 2 not taken.
6377 LogCvmfs(kLogCache, kLogDebug, "start transaction on %s has result %d",
616 template_path, transaction->fd);
617
1/2
✓ Branch 1 taken 6377 times.
✗ Branch 2 not taken.
6377 transaction->tmp_path = template_path;
618 6377 transaction->expected_size = size;
619 6377 return transaction->fd;
620 6469 }
621
622
623 600 manifest::Breadcrumb PosixCacheManager::LoadBreadcrumb(
624 const std::string &fqrn) {
625 600 return manifest::Manifest::ReadBreadcrumb(fqrn, cache_path_);
626 }
627
628
629 244 bool PosixCacheManager::StoreBreadcrumb(const manifest::Manifest &manifest) {
630 244 return manifest.ExportBreadcrumb(cache_path_, 0600);
631 }
632
633
634 bool PosixCacheManager::StoreBreadcrumb(std::string fqrn,
635 manifest::Breadcrumb breadcrumb) {
636 return breadcrumb.Export(fqrn, cache_path_, 0600);
637 }
638
639
640 176 void PosixCacheManager::TearDown2ReadOnly() {
641 176 cache_mode_ = kCacheReadOnly;
642
2/2
✓ Branch 1 taken 616 times.
✓ Branch 2 taken 132 times.
748 while (atomic_read32(&no_inflight_txns_) != 0)
643 616 SafeSleepMs(50);
644
645 132 QuotaManager *old_manager = quota_mgr_;
646
1/2
✓ Branch 2 taken 132 times.
✗ Branch 3 not taken.
132 quota_mgr_ = new NoopQuotaManager();
647
1/2
✓ Branch 0 taken 132 times.
✗ Branch 1 not taken.
132 delete old_manager;
648 132 }
649
650
651 6294 int64_t PosixCacheManager::Write(const void *buf, uint64_t size, void *txn) {
652 6294 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
653
654
2/2
✓ Branch 0 taken 4866 times.
✓ Branch 1 taken 1428 times.
6294 if (transaction->expected_size != kSizeUnknown) {
655
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 4822 times.
4866 if (transaction->size + size > transaction->expected_size) {
656 44 LogCvmfs(kLogCache, kLogDebug,
657 "Transaction size (%" PRIu64 ") > expected size (%" PRIu64 ")",
658 44 transaction->size + size, transaction->expected_size);
659 44 return -EFBIG;
660 }
661 }
662
663 6250 uint64_t written = 0;
664 6250 const unsigned char *read_pos = reinterpret_cast<const unsigned char *>(buf);
665
2/2
✓ Branch 0 taken 7380 times.
✓ Branch 1 taken 6206 times.
13586 while (written < size) {
666
2/2
✓ Branch 0 taken 2996 times.
✓ Branch 1 taken 4384 times.
7380 if (transaction->buf_pos == sizeof(transaction->buffer)) {
667
1/2
✓ Branch 1 taken 2996 times.
✗ Branch 2 not taken.
2996 const int retval = Flush(transaction);
668
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 2952 times.
2996 if (retval != 0) {
669 44 transaction->size += written;
670 44 return retval;
671 }
672 }
673 7336 const uint64_t remaining = size - written;
674 7336 const uint64_t space_in_buffer = sizeof(transaction->buffer)
675 7336 - transaction->buf_pos;
676 7336 const uint64_t batch_size = std::min(remaining, space_in_buffer);
677 7336 memcpy(transaction->buffer + transaction->buf_pos, read_pos, batch_size);
678 7336 transaction->buf_pos += batch_size;
679 7336 written += batch_size;
680 7336 read_pos += batch_size;
681 }
682 6206 transaction->size += written;
683 6206 return written;
684 }
685
686