GCC Code Coverage Report


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