GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_plugin/cvmfs_cache_null.cc
Date: 2026-05-03 02:36:16
Exec Total Coverage
Lines: 0 235 0.0%
Branches: 0 82 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * A demo external cache plugin. All data is stored in std::string. Feature-
5 * complete but quite inefficient.
6 */
7
8 #include <alloca.h>
9 #include <fcntl.h>
10 #include <inttypes.h>
11 #include <stdint.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15
16 #include <cassert>
17 #include <cstdio>
18 #include <cstring>
19 #include <ctime>
20 #include <map>
21 #include <string>
22 #include <vector>
23
24 #include "libcvmfs_cache.h"
25
26 using namespace std; // NOLINT
27
28 struct Object {
29 struct cvmcache_hash id;
30 string data;
31 cvmcache_object_type type;
32 int32_t refcnt;
33 string description;
34 };
35
36 struct TxnInfo {
37 struct cvmcache_hash id;
38 Object partial_object;
39 };
40
41 struct Listing {
42 Listing() : type(CVMCACHE_OBJECT_REGULAR), pos(0), elems(NULL) { }
43 cvmcache_object_type type;
44 uint64_t pos;
45 vector<Object> *elems;
46 };
47
48 struct ComparableHash {
49 ComparableHash() { memset(&hash, 0, sizeof(hash)); }
50 explicit ComparableHash(const struct cvmcache_hash &h) : hash(h) { }
51 struct cvmcache_hash hash;
52 bool operator<(const ComparableHash &other) const {
53 return cvmcache_hash_cmp(const_cast<cvmcache_hash *>(&(this->hash)),
54 const_cast<cvmcache_hash *>(&(other.hash)))
55 < 0;
56 }
57 };
58
59 map<uint64_t, TxnInfo> transactions;
60 map<ComparableHash, Object> storage;
61 map<uint64_t, Listing> listings;
62 map<std::string, cvmcache_breadcrumb> breadcrumbs;
63
64 struct cvmcache_context *ctx;
65
66 static int null_chrefcnt(struct cvmcache_hash *id, int32_t change_by) {
67 const ComparableHash h(*id);
68 if (storage.find(h) == storage.end())
69 return CVMCACHE_STATUS_NOENTRY;
70
71 Object obj = storage[h];
72 obj.refcnt += change_by;
73 if (obj.refcnt < 0)
74 return CVMCACHE_STATUS_BADCOUNT;
75 storage[h] = obj;
76 return CVMCACHE_STATUS_OK;
77 }
78
79
80 static int null_obj_info(struct cvmcache_hash *id,
81 struct cvmcache_object_info *info) {
82 const ComparableHash h(*id);
83 if (storage.find(h) == storage.end())
84 return CVMCACHE_STATUS_NOENTRY;
85
86 const Object obj = storage[h];
87 info->size = obj.data.length();
88 info->type = obj.type;
89 info->pinned = obj.refcnt > 0;
90 info->description = strdup(obj.description.c_str());
91 return CVMCACHE_STATUS_OK;
92 }
93
94
95 static int null_pread(struct cvmcache_hash *id,
96 uint64_t offset,
97 uint32_t *size,
98 unsigned char *buffer) {
99 const ComparableHash h(*id);
100 string data = storage[h].data;
101 if (offset > data.length())
102 return CVMCACHE_STATUS_OUTOFBOUNDS;
103 const unsigned nbytes = std::min(
104 *size, static_cast<uint32_t>(data.length() - offset));
105 memcpy(buffer, data.data() + offset, nbytes);
106 *size = nbytes;
107 return CVMCACHE_STATUS_OK;
108 }
109
110
111 static int null_start_txn(struct cvmcache_hash *id,
112 uint64_t txn_id,
113 struct cvmcache_object_info *info) {
114 Object partial_object;
115 partial_object.id = *id;
116 partial_object.type = info->type;
117 partial_object.refcnt = 1;
118 if (info->description != NULL)
119 partial_object.description = string(info->description);
120 TxnInfo txn;
121 txn.id = *id;
122 txn.partial_object = partial_object;
123 transactions[txn_id] = txn;
124 return CVMCACHE_STATUS_OK;
125 }
126
127
128 static int null_write_txn(uint64_t txn_id,
129 unsigned char *buffer,
130 uint32_t size) {
131 TxnInfo txn = transactions[txn_id];
132 txn.partial_object.data += string(reinterpret_cast<char *>(buffer), size);
133 transactions[txn_id] = txn;
134 return CVMCACHE_STATUS_OK;
135 }
136
137
138 static int null_commit_txn(uint64_t txn_id) {
139 const TxnInfo txn = transactions[txn_id];
140 const ComparableHash h(txn.id);
141 storage[h] = txn.partial_object;
142 transactions.erase(txn_id);
143 return CVMCACHE_STATUS_OK;
144 }
145
146 static int null_abort_txn(uint64_t txn_id) {
147 transactions.erase(txn_id);
148 return CVMCACHE_STATUS_OK;
149 }
150
151 static int null_info(struct cvmcache_info *info) {
152 info->size_bytes = uint64_t(-1);
153 info->used_bytes = info->pinned_bytes = 0;
154 for (map<ComparableHash, Object>::const_iterator i = storage.begin(),
155 i_end = storage.end();
156 i != i_end;
157 ++i) {
158 info->used_bytes += i->second.data.length();
159 if (i->second.refcnt > 0)
160 info->pinned_bytes += i->second.data.length();
161 }
162 info->no_shrink = 0;
163 return CVMCACHE_STATUS_OK;
164 }
165
166 static int null_shrink(uint64_t shrink_to, uint64_t *used) {
167 struct cvmcache_info info;
168 null_info(&info);
169 *used = info.used_bytes;
170 if (info.used_bytes <= shrink_to)
171 return CVMCACHE_STATUS_OK;
172
173 // Volatile objects
174 for (map<ComparableHash, Object>::iterator i = storage.begin(),
175 i_end = storage.end();
176 i != i_end;) {
177 if ((i->second.refcnt > 0)
178 || (i->second.type != CVMCACHE_OBJECT_VOLATILE)) {
179 ++i;
180 continue;
181 }
182 const unsigned length = i->second.data.length();
183 const map<ComparableHash, Object>::iterator delete_me = i++;
184 storage.erase(delete_me);
185 info.used_bytes -= length;
186 if (info.used_bytes <= shrink_to) {
187 *used = info.used_bytes;
188 return CVMCACHE_STATUS_OK;
189 }
190 }
191 // All other objects
192 for (map<ComparableHash, Object>::iterator i = storage.begin(),
193 i_end = storage.end();
194 i != i_end;) {
195 if (i->second.refcnt > 0) {
196 ++i;
197 continue;
198 }
199 const unsigned length = i->second.data.length();
200 const map<ComparableHash, Object>::iterator delete_me = i++;
201 storage.erase(delete_me);
202 info.used_bytes -= length;
203 if (info.used_bytes <= shrink_to) {
204 *used = info.used_bytes;
205 return CVMCACHE_STATUS_OK;
206 }
207 }
208
209 *used = info.used_bytes;
210 return CVMCACHE_STATUS_PARTIAL;
211 }
212
213 static int null_listing_begin(uint64_t lst_id, enum cvmcache_object_type type) {
214 Listing lst;
215 lst.type = type;
216 lst.elems = new vector<Object>();
217 for (map<ComparableHash, Object>::const_iterator i = storage.begin(),
218 i_end = storage.end();
219 i != i_end;
220 ++i) {
221 lst.elems->push_back(i->second);
222 }
223 listings[lst_id] = lst;
224 return CVMCACHE_STATUS_OK;
225 }
226
227 static int null_listing_next(int64_t listing_id,
228 struct cvmcache_object_info *item) {
229 Listing lst = listings[listing_id];
230 do {
231 if (lst.pos >= lst.elems->size())
232 return CVMCACHE_STATUS_OUTOFBOUNDS;
233
234 vector<Object> *elems = lst.elems;
235 if ((*elems)[lst.pos].type == lst.type) {
236 item->id = (*elems)[lst.pos].id;
237 item->size = (*elems)[lst.pos].data.length();
238 item->type = (*elems)[lst.pos].type;
239 item->pinned = (*elems)[lst.pos].refcnt > 0;
240 item->description = (*elems)[lst.pos].description.empty()
241 ? NULL
242 : strdup((*elems)[lst.pos].description.c_str());
243 break;
244 }
245 lst.pos++;
246 } while (true);
247 lst.pos++;
248 listings[listing_id] = lst;
249 return CVMCACHE_STATUS_OK;
250 }
251
252 static int null_listing_end(int64_t listing_id) {
253 delete listings[listing_id].elems;
254 listings.erase(listing_id);
255 return CVMCACHE_STATUS_OK;
256 }
257
258 static int null_breadcrumb_store(const char *fqrn,
259 const cvmcache_breadcrumb *breadcrumb) {
260 breadcrumbs[fqrn] = *breadcrumb;
261 return CVMCACHE_STATUS_OK;
262 }
263
264 static int null_breadcrumb_load(const char *fqrn,
265 cvmcache_breadcrumb *breadcrumb) {
266 const map<std::string, cvmcache_breadcrumb>::const_iterator
267 itr = breadcrumbs.find(fqrn);
268 if (itr == breadcrumbs.end())
269 return CVMCACHE_STATUS_NOENTRY;
270 *breadcrumb = itr->second;
271 return CVMCACHE_STATUS_OK;
272 }
273
274 static void Usage(const char *progname) {
275 printf("%s <config file>\n", progname);
276 }
277
278
279 int main(int argc, char **argv) {
280 if (argc < 2) {
281 Usage(argv[0]);
282 return 1;
283 }
284
285 cvmcache_init_global();
286
287 cvmcache_option_map *options = cvmcache_options_init();
288 if (cvmcache_options_parse(options, argv[1]) != 0) {
289 printf("cannot parse options file %s\n", argv[1]);
290 return 1;
291 }
292 char *locator = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_LOCATOR");
293 if (locator == NULL) {
294 printf("CVMFS_CACHE_PLUGIN_LOCATOR missing\n");
295 cvmcache_options_fini(options);
296 return 1;
297 }
298 char *test_mode = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_TEST");
299
300 if (!test_mode)
301 cvmcache_spawn_watchdog(NULL);
302
303 struct cvmcache_callbacks callbacks;
304 memset(&callbacks, 0, sizeof(callbacks));
305 callbacks.cvmcache_chrefcnt = null_chrefcnt;
306 callbacks.cvmcache_obj_info = null_obj_info;
307 callbacks.cvmcache_pread = null_pread;
308 callbacks.cvmcache_start_txn = null_start_txn;
309 callbacks.cvmcache_write_txn = null_write_txn;
310 callbacks.cvmcache_commit_txn = null_commit_txn;
311 callbacks.cvmcache_abort_txn = null_abort_txn;
312 callbacks.cvmcache_info = null_info;
313 callbacks.cvmcache_shrink = null_shrink;
314 callbacks.cvmcache_listing_begin = null_listing_begin;
315 callbacks.cvmcache_listing_next = null_listing_next;
316 callbacks.cvmcache_listing_end = null_listing_end;
317 callbacks.cvmcache_breadcrumb_store = null_breadcrumb_store;
318 callbacks.cvmcache_breadcrumb_load = null_breadcrumb_load;
319 callbacks.capabilities = CVMCACHE_CAP_ALL_V2;
320
321 ctx = cvmcache_init(&callbacks);
322 const int retval = cvmcache_listen(ctx, locator);
323 if (!retval) {
324 fprintf(stderr, "failed to listen on %s\n", locator);
325 return 1;
326 }
327
328 if (test_mode) {
329 // Daemonize, print out PID
330 pid_t pid;
331 int statloc;
332 if ((pid = fork()) == 0) {
333 if ((pid = fork()) == 0) {
334 const int null_read = open("/dev/null", O_RDONLY);
335 const int null_write = open("/dev/null", O_WRONLY);
336 assert((null_read >= 0) && (null_write >= 0));
337 int retval = dup2(null_read, 0);
338 assert(retval == 0);
339 retval = dup2(null_write, 1);
340 assert(retval == 1);
341 retval = dup2(null_write, 2);
342 assert(retval == 2);
343 close(null_read);
344 close(null_write);
345 } else {
346 assert(pid > 0);
347 printf("%d\n", pid);
348 fflush(stdout);
349 fsync(1);
350 _exit(0);
351 }
352 } else {
353 assert(pid > 0);
354 waitpid(pid, &statloc, 0);
355 _exit(0);
356 }
357 }
358
359 printf("Listening for cvmfs clients on %s\n", locator);
360 printf("NOTE: this process needs to run as user cvmfs\n\n");
361
362 // Starts the I/O processing thread
363 cvmcache_process_requests(ctx, 0);
364
365 if (test_mode)
366 while (true)
367 sleep(1);
368
369 if (!cvmcache_is_supervised()) {
370 printf("Press <R ENTER> to ask clients to release nested catalogs\n");
371 printf("Press <Ctrl+D> to quit\n");
372 while (true) {
373 char buf;
374 const int retval = read(fileno(stdin), &buf, 1);
375 if (retval != 1)
376 break;
377 if (buf == 'R') {
378 printf(" ... asking clients to release nested catalogs\n");
379 cvmcache_ask_detach(ctx);
380 }
381 }
382 cvmcache_terminate(ctx);
383 }
384
385 cvmcache_wait_for(ctx);
386 printf(" ... good bye\n");
387
388 cvmcache_options_free(locator);
389 cvmcache_options_fini(options);
390 cvmcache_terminate_watchdog();
391 cvmcache_cleanup_global();
392 return 0;
393 }
394