GCC Code Coverage Report


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