GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_posix.cc
Date: 2026-04-26 02:35:59
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 "crypto/hash.h"
50 #include "manifest.h"
51 #include "manifest_fetch.h"
52 #include "quota.h"
53 #include "util/atomic.h"
54 #include "util/logging.h"
55 #include "util/platform.h"
56 #include "util/posix.h"
57 #include "util/smalloc.h"
58
59 using namespace std; // NOLINT
60
61 namespace {
62
63 /**
64 * A CallGuard object can be placed at the beginning of a function. It counts
65 * the number of so-annotated functions that are in flight. The Drainout() call
66 * will wait until all functions that have been called so far are finished.
67 *
68 * The class is used in order to wait for remaining calls when switching into
69 * the read-only cache mode.
70 */
71 class CallGuard {
72 public:
73 CallGuard() {
74 const int32_t global_drainout = atomic_read32(&global_drainout_);
75 drainout_ = (global_drainout != 0);
76 if (!drainout_)
77 atomic_inc32(&num_inflight_calls_);
78 }
79 ~CallGuard() {
80 if (!drainout_)
81 atomic_dec32(&num_inflight_calls_);
82 }
83 static void Drainout() {
84 atomic_cas32(&global_drainout_, 0, 1);
85 while (atomic_read32(&num_inflight_calls_) != 0)
86 SafeSleepMs(50);
87 }
88
89 private:
90 bool drainout_;
91 static atomic_int32 global_drainout_;
92 static atomic_int32 num_inflight_calls_;
93 };
94 atomic_int32 CallGuard::num_inflight_calls_ = 0;
95 atomic_int32 CallGuard::global_drainout_ = 0;
96
97 } // anonymous namespace
98
99
100 //------------------------------------------------------------------------------
101
102
103 const uint64_t PosixCacheManager::kBigFile = 25 * 1024 * 1024; // 25M
104
105
106 758 int PosixCacheManager::AbortTxn(void *txn) {
107 758 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
108 758 LogCvmfs(kLogCache, kLogDebug, "abort %s", transaction->tmp_path.c_str());
109 758 close(transaction->fd);
110 758 const int result = unlink(transaction->tmp_path.c_str());
111 758 transaction->~Transaction();
112 758 atomic_dec32(&no_inflight_txns_);
113
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 674 times.
758 if (result == -1)
114 84 return -errno;
115 674 return 0;
116 }
117
118
119 /**
120 * This should only be used to replace the default NoopQuotaManager by a
121 * PosixQuotaManager. The cache manager takes the ownership of the passed
122 * quota manager.
123 */
124 742 bool PosixCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) {
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 742 times.
742 if (quota_mgr == NULL)
126 return false;
127
1/2
✓ Branch 0 taken 742 times.
✗ Branch 1 not taken.
742 delete quota_mgr_;
128 742 quota_mgr_ = quota_mgr;
129 742 return true;
130 }
131
132
133 2564 int PosixCacheManager::Close(int fd) {
134
1/2
✓ Branch 0 taken 2564 times.
✗ Branch 1 not taken.
2564 const int retval = do_refcount_ ? fd_mgr_->Close(fd) : close(fd);
135
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 2522 times.
2564 if (retval != 0)
136 42 return -errno;
137 2522 return 0;
138 }
139
140
141 5264 int PosixCacheManager::CommitTxn(void *txn) {
142 5264 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
143 int result;
144 5264 LogCvmfs(kLogCache, kLogDebug, "commit %s %s",
145 transaction->final_path.c_str(), transaction->tmp_path.c_str());
146
147 5264 result = Flush(transaction);
148 5264 close(transaction->fd);
149
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 5222 times.
5264 if (result < 0) {
150 42 unlink(transaction->tmp_path.c_str());
151 42 transaction->~Transaction();
152 42 atomic_dec32(&no_inflight_txns_);
153 42 return result;
154 }
155
156 // To support debugging, move files into quarantine on file size mismatch
157
2/2
✓ Branch 0 taken 1010 times.
✓ Branch 1 taken 4212 times.
5222 if (transaction->size != transaction->expected_size) {
158 // Allow size to be zero if alien cache, because hadoop-fuse-dfs returns
159 // size zero for a while
160
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 900 times.
1010 if ((transaction->expected_size != kSizeUnknown)
161
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
110 && (reports_correct_filesize_ || (transaction->size != 0))) {
162
1/3
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
110 LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
163 "size check failure for %s, expected %lu, got %lu",
164 220 transaction->id.ToString().c_str(), transaction->expected_size,
165 transaction->size);
166
1/2
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
110 CopyPath2Path(transaction->tmp_path,
167
2/4
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 110 times.
✗ Branch 6 not taken.
220 cache_path_ + "/quarantaine/" + transaction->id.ToString());
168 110 unlink(transaction->tmp_path.c_str());
169 110 transaction->~Transaction();
170 110 atomic_dec32(&no_inflight_txns_);
171 110 return -EIO;
172 }
173 }
174
175
2/2
✓ Branch 0 taken 5070 times.
✓ Branch 1 taken 42 times.
5112 if ((transaction->label.flags & kLabelPinned)
176
2/2
✓ Branch 0 taken 897 times.
✓ Branch 1 taken 4173 times.
5070 || (transaction->label.flags & kLabelCatalog)) {
177 2817 const bool retval = quota_mgr_->Pin(
178
1/2
✓ Branch 1 taken 939 times.
✗ Branch 2 not taken.
939 transaction->id, transaction->size, transaction->label.GetDescription(),
179 939 (transaction->label.flags & kLabelCatalog));
180
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 897 times.
939 if (!retval) {
181
1/2
✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
42 LogCvmfs(kLogCache, kLogDebug, "commit failed: cannot pin %s",
182 84 transaction->id.ToString().c_str());
183 42 unlink(transaction->tmp_path.c_str());
184 42 transaction->~Transaction();
185 42 atomic_dec32(&no_inflight_txns_);
186 42 return -ENOSPC;
187 }
188 }
189
190 // Move the temporary file into its final location
191
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 5028 times.
5070 if (alien_cache_) {
192 42 const int retval = chmod(transaction->tmp_path.c_str(), 0660);
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 assert(retval == 0);
194 }
195 5070 result = Rename(transaction->tmp_path.c_str(),
196 transaction->final_path.c_str());
197
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 4986 times.
5070 if (result < 0) {
198 84 LogCvmfs(kLogCache, kLogDebug, "commit failed: %s", strerror(errno));
199 84 unlink(transaction->tmp_path.c_str());
200
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 if ((transaction->label.flags & kLabelPinned)
201
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 42 times.
84 || (transaction->label.flags & kLabelCatalog)) {
202 42 quota_mgr_->Remove(transaction->id);
203 }
204 } else {
205 // Success, inform quota manager
206
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 4944 times.
4986 if (transaction->label.flags & kLabelVolatile) {
207
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 quota_mgr_->InsertVolatile(transaction->id, transaction->size,
208 84 transaction->label.GetDescription());
209 4944 } else if (!transaction->label.IsCatalog()
210
6/6
✓ Branch 0 taken 4131 times.
✓ Branch 1 taken 813 times.
✓ Branch 3 taken 4089 times.
✓ Branch 4 taken 42 times.
✓ Branch 5 taken 4089 times.
✓ Branch 6 taken 855 times.
4944 && !transaction->label.IsPinned()) {
211
1/2
✓ Branch 1 taken 4089 times.
✗ Branch 2 not taken.
4089 quota_mgr_->Insert(transaction->id, transaction->size,
212 8178 transaction->label.GetDescription());
213 }
214 }
215 5070 transaction->~Transaction();
216 5070 atomic_dec32(&no_inflight_txns_);
217 5070 return result;
218 }
219
220 6184 bool PosixCacheManager::InitCacheDirectory(const string &cache_path) {
221
1/2
✓ Branch 1 taken 6184 times.
✗ Branch 2 not taken.
6184 const FileSystemInfo fs_info = GetFileSystemInfo(cache_path);
222
223
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 6100 times.
6184 if (fs_info.type == kFsTypeTmpfs) {
224 84 is_tmpfs_ = true;
225 }
226
227
2/2
✓ Branch 0 taken 1363 times.
✓ Branch 1 taken 4821 times.
6184 if (alien_cache_) {
228
3/4
✓ Branch 1 taken 1363 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 1321 times.
1363 if (!MakeCacheDirectories(cache_path, 0770)) {
229 42 return false;
230 }
231
1/2
✓ Branch 1 taken 1321 times.
✗ Branch 2 not taken.
1321 LogCvmfs(kLogCache, kLogDebug | kLogSyslog,
232 "Cache directory structure created.");
233
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1321 times.
1321 switch (fs_info.type) {
234 case kFsTypeNFS:
235 rename_workaround_ = kRenameLink;
236 LogCvmfs(kLogCache, kLogDebug | kLogSyslog, "Alien cache is on NFS.");
237 break;
238 case kFsTypeBeeGFS:
239 rename_workaround_ = kRenameSamedir;
240 LogCvmfs(kLogCache, kLogDebug | kLogSyslog,
241 "Alien cache is on BeeGFS.");
242 break;
243 1321 default:
244 1321 break;
245 }
246 } else {
247
3/4
✓ Branch 1 taken 4821 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 4779 times.
4821 if (!MakeCacheDirectories(cache_path, 0700))
248 42 return false;
249 }
250
251 // TODO(jblomer): we might not need to look anymore for cvmfs 2.0 relicts
252
4/7
✓ Branch 1 taken 6100 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6100 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 42 times.
✓ Branch 8 taken 6058 times.
6100 if (FileExists(cache_path + "/cvmfscatalog.cache")) {
253
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 LogCvmfs(kLogCache, kLogDebug | kLogSyslogErr,
254 "Not mounting on cvmfs 2.0.X cache");
255 42 return false;
256 }
257 6058 return true;
258 }
259
260 6184 PosixCacheManager *PosixCacheManager::Create(
261 const string &cache_path, const bool alien_cache,
262 const RenameWorkarounds rename_workaround, const bool do_refcount,
263 const bool cleanup_unused_first) {
264 UniquePtr<PosixCacheManager> cache_manager(new PosixCacheManager(
265
3/6
✓ Branch 1 taken 6184 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6184 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6184 times.
✗ Branch 8 not taken.
6184 cache_path, alien_cache, do_refcount, cleanup_unused_first));
266
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6184 times.
6184 assert(cache_manager.IsValid());
267
268 6184 cache_manager->rename_workaround_ = rename_workaround;
269
270
1/2
✓ Branch 2 taken 6184 times.
✗ Branch 3 not taken.
6184 const bool result_ = cache_manager->InitCacheDirectory(cache_path);
271
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 6058 times.
6184 if (!result_) {
272 126 return NULL;
273 }
274
275 6058 return cache_manager.Release();
276 6184 }
277
278
279 4930 void PosixCacheManager::CtrlTxn(const Label &label,
280 const int flags,
281 void *txn) {
282 4930 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
283 4930 transaction->label = label;
284 4930 }
285
286
287 string PosixCacheManager::Describe() {
288 string msg;
289 if (do_refcount_) {
290 msg = "Refcounting Posix cache manager"
291 "(cache directory: "
292 + cache_path_ + ")\n";
293 } else {
294 msg = "Posix cache manager (cache directory: " + cache_path_ + ")\n";
295 }
296 return msg;
297 }
298
299
300 /**
301 * If not refcounting, nothing to do, the kernel keeps the state
302 * of open file descriptors. Return a dummy memory location.
303 */
304 39 void *PosixCacheManager::DoSaveState() {
305
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 if (do_refcount_) {
306
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 SavedState *state = new SavedState();
307 39 state->fd_mgr = fd_mgr_->Clone();
308 39 return state;
309 }
310 char *c = reinterpret_cast<char *>(smalloc(1));
311 *c = kMagicNoRefcount;
312 return c;
313 }
314
315
316 39 int PosixCacheManager::DoRestoreState(void *data) {
317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 assert(data);
318
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 if (do_refcount_) {
319 39 SavedState *state = reinterpret_cast<SavedState *>(data);
320
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 if (state->magic_number == kMagicRefcount) {
321 39 LogCvmfs(kLogCache, kLogDebug,
322 "Restoring refcount cache manager from "
323 "refcounted posix cache manager");
324
325 39 fd_mgr_->AssignFrom(state->fd_mgr.weak_ref());
326 } else {
327 LogCvmfs(kLogCache, kLogDebug,
328 "Restoring refcount cache manager from "
329 "non-refcounted posix cache manager");
330 }
331 39 return -1;
332 }
333
334 char *c = reinterpret_cast<char *>(data);
335 assert(*c == kMagicNoRefcount || *c == kMagicRefcount);
336 if (*c == kMagicRefcount) {
337 SavedState *state = reinterpret_cast<SavedState *>(data);
338 LogCvmfs(kLogCache, kLogDebug,
339 "Restoring non-refcount cache manager from "
340 "refcounted posix cache manager - this "
341 " is not possible, keep refcounting.");
342 fd_mgr_->AssignFrom(state->fd_mgr.weak_ref());
343 do_refcount_ = true;
344 }
345 return -1;
346 }
347
348
349 39 bool PosixCacheManager::DoFreeState(void *data) {
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 assert(data);
351 39 SavedState *state = reinterpret_cast<SavedState *>(data);
352
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 if (state->magic_number == kMagicRefcount) {
353
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 delete state;
354 } else {
355 // If not refcounted, the state is the dummy SavedState
356 // of the regular posix cache manager
357 free(data);
358 }
359 39 return true;
360 }
361
362
363 152 int PosixCacheManager::Dup(int fd) {
364
1/2
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
152 const int new_fd = do_refcount_ ? fd_mgr_->Dup(fd) : dup(fd);
365
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 76 times.
152 if (new_fd < 0)
366 76 return -errno;
367 76 return new_fd;
368 }
369
370
371 10028 int PosixCacheManager::Flush(Transaction *transaction) {
372
2/2
✓ Branch 0 taken 2456 times.
✓ Branch 1 taken 7572 times.
10028 if (transaction->buf_pos == 0)
373 2456 return 0;
374 15144 const int written = write(transaction->fd, transaction->buffer,
375 7572 transaction->buf_pos);
376
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 7446 times.
7572 if (written < 0)
377 126 return -errno;
378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7446 times.
7446 if (static_cast<unsigned>(written) != transaction->buf_pos) {
379 transaction->buf_pos -= written;
380 return -EIO;
381 }
382 7446 transaction->buf_pos = 0;
383 7446 return 0;
384 }
385
386
387 10929 inline string PosixCacheManager::GetPathInCache(const shash::Any &id) {
388
2/4
✓ Branch 2 taken 10929 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10929 times.
✗ Branch 6 not taken.
21858 return cache_path_ + "/" + id.MakePathWithoutSuffix();
389 }
390
391
392 2093 int64_t PosixCacheManager::GetSize(int fd) {
393 platform_stat64 info;
394 2093 const int retval = platform_fstat(fd, &info);
395
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 2051 times.
2093 if (retval != 0)
396 42 return -errno;
397 2051 return info.st_size;
398 }
399
400
401 4831 int PosixCacheManager::Open(const LabeledObject &object) {
402
1/2
✓ Branch 1 taken 4831 times.
✗ Branch 2 not taken.
4831 const string path = GetPathInCache(object.id);
403 int result;
404
1/2
✓ Branch 0 taken 4831 times.
✗ Branch 1 not taken.
4831 if (do_refcount_) {
405
1/2
✓ Branch 2 taken 4831 times.
✗ Branch 3 not taken.
4831 result = fd_mgr_->Open(object.id, path);
406 } else {
407 result = open(path.c_str(), O_RDONLY);
408 }
409
2/2
✓ Branch 0 taken 1564 times.
✓ Branch 1 taken 3267 times.
4831 if (result >= 0) {
410
1/2
✓ Branch 2 taken 1564 times.
✗ Branch 3 not taken.
1564 LogCvmfs(kLogCache, kLogDebug, "hit %s", path.c_str());
411 // platform_disable_kcache(result);
412
1/2
✓ Branch 1 taken 1564 times.
✗ Branch 2 not taken.
1564 quota_mgr_->Touch(object.id);
413 } else {
414 3267 result = -errno;
415
1/2
✓ Branch 2 taken 3267 times.
✗ Branch 3 not taken.
3267 LogCvmfs(kLogCache, kLogDebug, "miss %s (%d)", path.c_str(), result);
416 }
417 4831 return result;
418 4831 }
419
420
421 1204 int PosixCacheManager::OpenFromTxn(void *txn) {
422 1204 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
423 1204 const int retval = Flush(transaction);
424
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1162 times.
1204 if (retval < 0)
425 42 return retval;
426 int fd_rdonly;
427
428
1/2
✓ Branch 0 taken 1162 times.
✗ Branch 1 not taken.
1162 if (do_refcount_) {
429
2/4
✓ Branch 4 taken 1162 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1162 times.
✗ Branch 8 not taken.
1162 fd_rdonly = fd_mgr_->Open(transaction->id, transaction->tmp_path.c_str());
430 } else {
431 fd_rdonly = open(transaction->tmp_path.c_str(), O_RDONLY);
432 }
433
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1120 times.
1162 if (fd_rdonly == -1)
434 42 return -errno;
435 1120 return fd_rdonly;
436 }
437
438
439 14681 int64_t PosixCacheManager::Pread(int fd,
440 void *buf,
441 uint64_t size,
442 uint64_t offset) {
443 int64_t result;
444 do {
445 14681 errno = 0;
446 14681 result = pread(fd, buf, size, offset);
447
3/4
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 14597 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
14681 } while ((result == -1) && (errno == EINTR));
448
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 14597 times.
14681 if (result < 0)
449 84 return -errno;
450 14597 return result;
451 }
452
453
454 5322 int PosixCacheManager::Rename(const char *oldpath, const char *newpath) {
455 int result;
456
2/2
✓ Branch 0 taken 5196 times.
✓ Branch 1 taken 126 times.
5322 if (rename_workaround_ != kRenameLink) {
457 5196 result = rename(oldpath, newpath);
458
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 5070 times.
5196 if (result < 0)
459 126 return -errno;
460 5070 return 0;
461 }
462
463 126 result = link(oldpath, newpath);
464
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 42 times.
126 if (result < 0) {
465
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 42 times.
84 if (errno == EEXIST) {
466 42 LogCvmfs(kLogCache, kLogDebug, "%s already existed, ignoring", newpath);
467 } else {
468 42 return -errno;
469 }
470 }
471 84 result = unlink(oldpath);
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
84 if (result < 0)
473 return -errno;
474 84 return 0;
475 }
476
477
478 /**
479 * Used by the sqlite vfs in order to preload file catalogs into the file system
480 * buffers.
481 *
482 * No-op if the fd is to a file that is on a tmpfs, and so already in page cache
483 */
484 940 int PosixCacheManager::Readahead(int fd) {
485 unsigned char *buf[4096];
486 int nbytes;
487 940 uint64_t pos = 0;
488
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 940 times.
940 if (is_tmpfs()) {
489 return 0;
490 }
491 do {
492
1/2
✓ Branch 1 taken 4484 times.
✗ Branch 2 not taken.
4484 nbytes = Pread(fd, buf, 4096, pos);
493 4484 pos += nbytes;
494
2/2
✓ Branch 0 taken 3544 times.
✓ Branch 1 taken 940 times.
4484 } while (nbytes == 4096);
495
1/2
✓ Branch 1 taken 940 times.
✗ Branch 2 not taken.
940 LogCvmfs(kLogCache, kLogDebug, "read-ahead %d, %" PRIu64, fd, pos);
496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 940 times.
940 if (nbytes < 0)
497 return nbytes;
498 940 return 0;
499 }
500
501
502 330 int PosixCacheManager::Reset(void *txn) {
503 330 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
504 330 transaction->buf_pos = 0;
505 330 transaction->size = 0;
506 330 int retval = lseek(transaction->fd, 0, SEEK_SET);
507
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 288 times.
330 if (retval < 0)
508 42 return -errno;
509 288 retval = ftruncate(transaction->fd, 0);
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 288 times.
288 if (retval < 0)
511 return -errno;
512 288 return 0;
513 }
514
515
516 6182 int PosixCacheManager::StartTxn(const shash::Any &id,
517 uint64_t size,
518 void *txn) {
519 6182 atomic_inc32(&no_inflight_txns_);
520
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 6140 times.
6182 if (cache_mode_ == kCacheReadOnly) {
521 42 atomic_dec32(&no_inflight_txns_);
522 42 return -EROFS;
523 }
524
525
2/2
✓ Branch 0 taken 4994 times.
✓ Branch 1 taken 1146 times.
6140 if (size != kSizeUnknown) {
526
3/4
✓ Branch 1 taken 4994 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 4952 times.
4994 if (size > quota_mgr_->GetMaxFileSize()) {
527
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 LogCvmfs(kLogCache, kLogDebug,
528 "file too big for lru cache (%" PRIu64 " "
529 "requested but only %" PRIu64 " bytes free)",
530
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 size, quota_mgr_->GetMaxFileSize());
531 42 atomic_dec32(&no_inflight_txns_);
532 42 return -ENOSPC;
533 }
534
535 // For large files, ensure enough free cache space before writing the chunk
536
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 4910 times.
4952 if (size > kBigFile) {
537
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 const uint64_t cache_size = quota_mgr_->GetSize();
538
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 const uint64_t cache_capacity = quota_mgr_->GetCapacity();
539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 assert(cache_capacity >= size);
540
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 if ((cache_size + size) > cache_capacity) {
541 84 const uint64_t leave_size = std::min(cache_capacity / 2,
542 42 cache_capacity - size);
543
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 quota_mgr_->Cleanup(leave_size);
544 }
545 }
546 }
547
548
1/2
✓ Branch 1 taken 6098 times.
✗ Branch 2 not taken.
6098 string path_in_cache = GetPathInCache(id);
549
1/2
✓ Branch 2 taken 6098 times.
✗ Branch 3 not taken.
6098 Transaction *transaction = new (txn) Transaction(id, path_in_cache);
550
551 6098 char *template_path = NULL;
552 6098 unsigned temp_path_len = 0;
553
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 6014 times.
6098 if (rename_workaround_ == kRenameSamedir) {
554 84 temp_path_len = path_in_cache.length() + 6;
555 84 template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1));
556 84 memcpy(template_path, path_in_cache.data(), path_in_cache.length());
557 84 memset(template_path + path_in_cache.length(), 'X', 6);
558 } else {
559 6014 temp_path_len = txn_template_path_.length();
560 6014 template_path = reinterpret_cast<char *>(alloca(temp_path_len + 1));
561
1/2
✓ Branch 1 taken 6014 times.
✗ Branch 2 not taken.
6014 memcpy(template_path, &txn_template_path_[0], temp_path_len);
562 }
563 6098 template_path[temp_path_len] = '\0';
564
565
1/2
✓ Branch 1 taken 6098 times.
✗ Branch 2 not taken.
6098 transaction->fd = mkstemp(template_path);
566
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 6022 times.
6098 if (transaction->fd == -1) {
567 76 transaction->~Transaction();
568 76 atomic_dec32(&no_inflight_txns_);
569 76 return -errno;
570 }
571
572
1/2
✓ Branch 1 taken 6022 times.
✗ Branch 2 not taken.
6022 LogCvmfs(kLogCache, kLogDebug, "start transaction on %s has result %d",
573 template_path, transaction->fd);
574
1/2
✓ Branch 1 taken 6022 times.
✗ Branch 2 not taken.
6022 transaction->tmp_path = template_path;
575 6022 transaction->expected_size = size;
576 6022 return transaction->fd;
577 6098 }
578
579
580 569 manifest::Breadcrumb PosixCacheManager::LoadBreadcrumb(
581 const std::string &fqrn) {
582 569 return manifest::Manifest::ReadBreadcrumb(fqrn, cache_path_);
583 }
584
585
586 240 bool PosixCacheManager::StoreBreadcrumb(const manifest::Manifest &manifest) {
587 240 return manifest.ExportBreadcrumb(cache_path_, 0600);
588 }
589
590
591 bool PosixCacheManager::StoreBreadcrumb(std::string fqrn,
592 manifest::Breadcrumb breadcrumb) {
593 return breadcrumb.Export(fqrn, cache_path_, 0600);
594 }
595
596
597 168 void PosixCacheManager::TearDown2ReadOnly() {
598 168 cache_mode_ = kCacheReadOnly;
599
2/2
✓ Branch 1 taken 588 times.
✓ Branch 2 taken 126 times.
714 while (atomic_read32(&no_inflight_txns_) != 0)
600 588 SafeSleepMs(50);
601
602 126 QuotaManager *old_manager = quota_mgr_;
603
1/2
✓ Branch 2 taken 126 times.
✗ Branch 3 not taken.
126 quota_mgr_ = new NoopQuotaManager();
604
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 delete old_manager;
605 126 }
606
607
608 6195 int64_t PosixCacheManager::Write(const void *buf, uint64_t size, void *txn) {
609 6195 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
610
611
2/2
✓ Branch 0 taken 4490 times.
✓ Branch 1 taken 1705 times.
6195 if (transaction->expected_size != kSizeUnknown) {
612
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 4448 times.
4490 if (transaction->size + size > transaction->expected_size) {
613 42 LogCvmfs(kLogCache, kLogDebug,
614 "Transaction size (%" PRIu64 ") > expected size (%" PRIu64 ")",
615 42 transaction->size + size, transaction->expected_size);
616 42 return -EFBIG;
617 }
618 }
619
620 6153 uint64_t written = 0;
621 6153 const unsigned char *read_pos = reinterpret_cast<const unsigned char *>(buf);
622
2/2
✓ Branch 0 taken 7766 times.
✓ Branch 1 taken 6111 times.
13877 while (written < size) {
623
2/2
✓ Branch 0 taken 3560 times.
✓ Branch 1 taken 4206 times.
7766 if (transaction->buf_pos == sizeof(transaction->buffer)) {
624
1/2
✓ Branch 1 taken 3560 times.
✗ Branch 2 not taken.
3560 const int retval = Flush(transaction);
625
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 3518 times.
3560 if (retval != 0) {
626 42 transaction->size += written;
627 42 return retval;
628 }
629 }
630 7724 const uint64_t remaining = size - written;
631 7724 const uint64_t space_in_buffer = sizeof(transaction->buffer)
632 7724 - transaction->buf_pos;
633 7724 const uint64_t batch_size = std::min(remaining, space_in_buffer);
634 7724 memcpy(transaction->buffer + transaction->buf_pos, read_pos, batch_size);
635 7724 transaction->buf_pos += batch_size;
636 7724 written += batch_size;
637 7724 read_pos += batch_size;
638 }
639 6111 transaction->size += written;
640 6111 return written;
641 }
642
643