GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache.cc
Date: 2026-10-04 02:40:33
Exec Total Coverage
Lines: 95 115 82.6%
Branches: 60 124 48.4%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "cache.h"
6
7 #include <alloca.h>
8 #include <errno.h>
9
10 #include <cassert>
11 #include <cstdlib>
12 #include <string>
13
14 #include "compression/compression.h"
15 #include "crypto/hash.h"
16 #include "quota.h"
17 #include "util/posix.h"
18 #include "util/smalloc.h"
19
20 using namespace std; // NOLINT
21
22 const uint64_t CacheManager::kSizeUnknown = uint64_t(-1);
23
24
25
1/2
✓ Branch 3 taken 7123 times.
✗ Branch 4 not taken.
7123 CacheManager::CacheManager() : quota_mgr_(new NoopQuotaManager()) { }
26
27
28
2/2
✓ Branch 0 taken 7061 times.
✓ Branch 1 taken 48 times.
14218 CacheManager::~CacheManager() { delete quota_mgr_; }
29
30
31 /**
32 * Compresses and checksums the file pointed to by fd. The hash algorithm needs
33 * to be set in id.
34 */
35 184 int CacheManager::ChecksumFd(int fd, shash::Any *id) {
36
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 shash::ContextPtr hash_context(id->algorithm);
37 184 hash_context.buffer = alloca(hash_context.size);
38
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 shash::Init(hash_context);
39
40 z_stream strm;
41
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 zlib::CompressInit(&strm);
42 zlib::StreamStates retval;
43
44 unsigned char buf[4096];
45 184 uint64_t pos = 0;
46 bool eof;
47
48 do {
49
1/2
✓ Branch 1 taken 230 times.
✗ Branch 2 not taken.
230 const int64_t nbytes = Pread(fd, buf, 4096, pos);
50
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 184 times.
230 if (nbytes < 0) {
51
1/2
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
46 zlib::CompressFini(&strm);
52 46 return nbytes;
53 }
54 184 pos += nbytes;
55 184 eof = nbytes < 4096;
56
1/2
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
184 retval = zlib::CompressZStream2Null(buf, nbytes, eof, &strm, &hash_context);
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 184 times.
184 if (retval == zlib::kStreamDataError) {
58 ✗ zlib::CompressFini(&strm);
59 ✗ return -EINVAL;
60 }
61
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 138 times.
184 } while (!eof);
62
63
1/2
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
138 zlib::CompressFini(&strm);
64
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 138 times.
138 if (retval != zlib::kStreamEnd)
65 ✗ return -EINVAL;
66
1/2
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
138 shash::Final(hash_context, id);
67 138 return 0;
68 }
69
70
71 /**
72 * Commits the memory blob buffer to the given chunk id. No checking!
73 * The hash and the memory blob need to match.
74 */
75 5929 bool CacheManager::CommitFromMem(const LabeledObject &object,
76 const unsigned char *buffer,
77 const uint64_t size) {
78 5929 void *txn = alloca(this->SizeOfTxn());
79 5929 const int fd = this->StartTxn(object.id, size, txn);
80
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5917 times.
5929 if (fd < 0)
81 12 return false;
82 5917 this->CtrlTxn(object.label, 0, txn);
83 5917 int64_t retval = this->Write(buffer, size, txn);
84
3/4
✓ Branch 0 taken 5871 times.
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5871 times.
5917 if ((retval < 0) || (static_cast<uint64_t>(retval) != size)) {
85 46 this->AbortTxn(txn);
86 46 return false;
87 }
88 5871 retval = this->CommitTxn(txn);
89 5871 return retval == 0;
90 }
91
92
93 110 void CacheManager::FreeState(const int fd_progress, void *data) {
94 110 State *state = reinterpret_cast<State *>(data);
95
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 24 times.
110 if (fd_progress >= 0)
96
2/4
✓ Branch 2 taken 86 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 86 times.
✗ Branch 6 not taken.
86 SendMsg2Socket(fd_progress, "Releasing saved open files table\n");
97
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 110 times.
110 assert(state->version == kStateVersion);
98
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
110 assert(state->manager_type == id());
99 110 const bool result = DoFreeState(state->concrete_state);
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 110 times.
110 if (!result) {
101 ✗ if (fd_progress >= 0) {
102 ✗ SendMsg2Socket(fd_progress,
103 " *** Releasing open files table failed!\n");
104 }
105 ✗ abort();
106 }
107
1/2
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
110 delete state;
108 110 }
109
110
111 /**
112 * Tries to open a file and copies its contents into a newly malloc'd
113 * memory area. User of the function has to free buffer (if successful).
114 *
115 * @param[in] id content hash of the catalog entry.
116 * @param[out] buffer Contents of the file
117 * @param[out] size Size of the file
118 * \return True if successful, false otherwise.
119 */
120 486 bool CacheManager::Open2Mem(const LabeledObject &object,
121 unsigned char **buffer,
122 uint64_t *size) {
123 486 *size = 0;
124 486 *buffer = NULL;
125
126 486 const int fd = this->Open(object);
127
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 376 times.
486 if (fd < 0)
128 110 return false;
129
130 376 const int64_t s = this->GetSize(fd);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 376 times.
376 assert(s >= 0);
132 376 *size = static_cast<uint64_t>(s);
133
134 376 int64_t retval = 0;
135
2/2
✓ Branch 0 taken 316 times.
✓ Branch 1 taken 60 times.
376 if (*size > 0) {
136 316 *buffer = static_cast<unsigned char *>(smalloc(*size));
137 316 retval = this->Pread(fd, *buffer, *size, 0);
138 } else {
139 60 *buffer = NULL;
140 }
141
142 376 this->Close(fd);
143
3/4
✓ Branch 0 taken 330 times.
✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 330 times.
376 if ((retval < 0) || (static_cast<uint64_t>(retval) != *size)) {
144 46 free(*buffer);
145 46 *buffer = NULL;
146 46 *size = 0;
147 46 return false;
148 }
149 330 return true;
150 }
151
152
153 /**
154 * Uses the regular open and, if the file exists in the cache, pins it. There
155 * is a race condition: the file can be removed between the open and the Pin.
156 * This is fixed by the quota manager's unpin method that removes files which
157 * do not exist anymore in the cache. (The quota manager also translates double
158 * pins into a no-op, so that the accounting does not get out of sync.)
159 */
160 1982 int CacheManager::OpenPinned(const LabeledObject &object) {
161 1982 const int fd = this->Open(object);
162
2/2
✓ Branch 0 taken 386 times.
✓ Branch 1 taken 1596 times.
1982 if (fd >= 0) {
163 386 const int64_t size = this->GetSize(fd);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 386 times.
386 if (size < 0) {
165 ✗ this->Close(fd);
166 ✗ return size;
167 }
168
1/2
✓ Branch 1 taken 386 times.
✗ Branch 2 not taken.
386 const bool retval = quota_mgr_->Pin(object.id, static_cast<uint64_t>(size),
169 772 object.label.GetDescription(),
170 386 object.label.IsCatalog());
171
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 340 times.
386 if (!retval) {
172 46 this->Close(fd);
173 46 return -ENOSPC;
174 }
175 }
176 1936 return fd;
177 }
178
179
180 112 int CacheManager::RestoreState(const int fd_progress, void *data) {
181 112 State *state = reinterpret_cast<State *>(data);
182
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 24 times.
112 if (fd_progress >= 0)
183
2/4
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 88 times.
✗ Branch 6 not taken.
88 SendMsg2Socket(fd_progress, "Restoring open files table... ");
184
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (state->version != kStateVersion) {
185 ✗ if (fd_progress >= 0)
186 ✗ SendMsg2Socket(fd_progress, "unsupported state version!\n");
187 ✗ abort();
188 }
189
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 112 times.
112 if (state->manager_type != id()) {
190 ✗ if (fd_progress >= 0)
191 ✗ SendMsg2Socket(fd_progress, "switching cache manager unsupported!\n");
192 ✗ abort();
193 }
194 112 const int new_root_fd = DoRestoreState(state->concrete_state);
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (new_root_fd < -1) {
196 ✗ if (fd_progress >= 0)
197 ✗ SendMsg2Socket(fd_progress, "FAILED!\n");
198 ✗ abort();
199 }
200
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 24 times.
112 if (fd_progress >= 0)
201
2/4
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 88 times.
✗ Branch 6 not taken.
88 SendMsg2Socket(fd_progress, "done\n");
202 112 return new_root_fd;
203 }
204
205
206 /**
207 * The actual work is done in the concrete cache managers.
208 */
209 112 void *CacheManager::SaveState(const int fd_progress) {
210
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 24 times.
112 if (fd_progress >= 0)
211
2/4
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 88 times.
✗ Branch 6 not taken.
88 SendMsg2Socket(fd_progress, "Saving open files table\n");
212 112 State *state = new State();
213 112 state->manager_type = id();
214 112 state->concrete_state = DoSaveState();
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (state->concrete_state == NULL) {
216 ✗ if (fd_progress >= 0) {
217 ✗ SendMsg2Socket(
218 fd_progress,
219 " *** This cache manager does not support saving state!\n");
220 }
221 ✗ abort();
222 }
223 112 return state;
224 }
225