GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache.cc
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 95 114 83.3%
Branches: 60 124 48.4%

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