GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_posix.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 281 320 87.8%
Branches: 165 267 61.8%

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