GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/libcvmfs.cc
Date: 2026-05-03 02:36:16
Exec Total Coverage
Lines: 155 278 55.8%
Branches: 84 258 32.6%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * libcvmfs provides an API for the CernVM-FS client. This is an
5 * alternative to FUSE for reading a remote CernVM-FS repository.
6 */
7
8 #define _FILE_OFFSET_BITS 64
9
10
11 #include "libcvmfs.h"
12
13 #include <errno.h>
14 #include <inttypes.h>
15 #include <stddef.h>
16 #include <sys/stat.h>
17
18 #include <cassert>
19 #include <cstdlib>
20 #include <string>
21
22 #include "libcvmfs_int.h"
23 #include "statistics.h"
24 #include "util/logging.h"
25 #include "util/posix.h"
26 #include "xattr.h"
27
28 using namespace std; // NOLINT
29
30 /**
31 * Create the cvmfs_attr struct which contains the same information
32 * as a stat, but also has pointers to the hash, symlink, and name.
33 */
34 105 struct cvmfs_attr *cvmfs_attr_init() {
35 struct cvmfs_attr *attr;
36 105 attr = reinterpret_cast<cvmfs_attr *>(calloc(1, sizeof(*attr)));
37 105 attr->version = 1;
38 105 attr->size = sizeof(*attr);
39 105 return attr;
40 }
41
42
43 /**
44 * Destroy the cvmfs_attr struct and frees the checksum, symlink,
45 * name, and xattrs.
46 */
47 105 void cvmfs_attr_free(struct cvmfs_attr *attr) {
48
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
105 if (attr) {
49 105 free(attr->cvm_checksum);
50 105 free(attr->cvm_symlink);
51 105 free(attr->cvm_name);
52 105 free(attr->cvm_parent);
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 delete reinterpret_cast<XattrList *>(attr->cvm_xattrs);
54 }
55 105 free(attr);
56 105 }
57
58
59 105 struct cvmfs_nc_attr *cvmfs_nc_attr_init() {
60 struct cvmfs_nc_attr *attr;
61 105 attr = reinterpret_cast<cvmfs_nc_attr *>(calloc(1, sizeof(*attr)));
62 105 return attr;
63 }
64
65 105 void cvmfs_nc_attr_free(struct cvmfs_nc_attr *nc_attr) {
66
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
105 if (nc_attr) {
67 105 free(nc_attr->mountpoint);
68 105 free(nc_attr->hash);
69 }
70 105 free(nc_attr);
71 105 }
72
73
74 /**
75 * Expand symlinks in all levels of a path. Also, expand ".." and ".". This
76 * also has the side-effect of ensuring that cvmfs_getattr() is called on all
77 * parent paths, which is needed to ensure proper loading of nested catalogs
78 * before the child is accessed.
79 */
80 770 static int expand_path(const int depth,
81 LibContext *ctx,
82 char const *path,
83 string *expanded_path) {
84
2/4
✓ Branch 2 taken 770 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 770 times.
✗ Branch 6 not taken.
1540 const string p_path = GetParentPath(path);
85
2/4
✓ Branch 2 taken 770 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 770 times.
✗ Branch 6 not taken.
1540 const string fname = GetFileName(path);
86 int rc;
87
88
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 770 times.
770 if (fname == "..") {
89 rc = expand_path(depth, ctx, p_path.c_str(), expanded_path);
90 if (rc != 0) {
91 return -1;
92 }
93 if (*expanded_path == "/") {
94 // attempt to access parent path of the root of the repository
95 LogCvmfs(kLogCvmfs, kLogDebug,
96 "libcvmfs cannot resolve symlinks to paths outside of the repo: "
97 "%s",
98 path);
99 errno = ENOENT;
100 return -1;
101 }
102 *expanded_path = GetParentPath(*expanded_path);
103 if (*expanded_path == "") {
104 *expanded_path = "/";
105 }
106 return 0;
107 }
108
109 770 string buf;
110
2/2
✓ Branch 1 taken 315 times.
✓ Branch 2 taken 455 times.
770 if (p_path != "") {
111
1/2
✓ Branch 2 taken 315 times.
✗ Branch 3 not taken.
315 rc = expand_path(depth, ctx, p_path.c_str(), &buf);
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 315 times.
315 if (rc != 0) {
113 return -1;
114 }
115
116
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 315 times.
315 if (fname == ".") {
117 *expanded_path = buf;
118 return 0;
119 }
120 }
121
122
5/8
✓ Branch 1 taken 315 times.
✓ Branch 2 taken 455 times.
✓ Branch 5 taken 315 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 315 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 770 times.
✗ Branch 10 not taken.
770 if (buf.length() == 0 || buf[buf.length() - 1] != '/') {
123
1/2
✓ Branch 1 taken 770 times.
✗ Branch 2 not taken.
770 buf += "/";
124 }
125
1/2
✓ Branch 1 taken 770 times.
✗ Branch 2 not taken.
770 buf += fname;
126
127 struct stat st;
128
1/2
✓ Branch 2 taken 770 times.
✗ Branch 3 not taken.
770 rc = ctx->GetAttr(buf.c_str(), &st);
129
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 735 times.
770 if (rc != 0) {
130 35 errno = -rc;
131 35 return -1;
132 }
133
134
1/2
✓ Branch 0 taken 735 times.
✗ Branch 1 not taken.
735 if (!S_ISLNK(st.st_mode)) {
135
1/2
✓ Branch 1 taken 735 times.
✗ Branch 2 not taken.
735 *expanded_path = buf;
136 735 return 0;
137 }
138
139 if (depth > 1000) {
140 // avoid unbounded recursion due to symlinks
141 LogCvmfs(kLogCvmfs, kLogDebug,
142 "libcvmfs hit its symlink recursion limit: %s", path);
143 errno = ELOOP;
144 return -1;
145 }
146
147 // expand symbolic link
148
149 char *ln_buf = reinterpret_cast<char *>(alloca(st.st_size + 2));
150 if (!ln_buf) {
151 errno = ENOMEM;
152 return -1;
153 }
154 rc = ctx->Readlink(buf.c_str(), ln_buf, st.st_size + 2);
155 if (rc != 0) {
156 errno = -rc;
157 return -1;
158 }
159 if (ln_buf[0] == '/') {
160 // symlink is absolute path, strip /cvmfs/$repo
161 const unsigned len = ctx->mount_point()->fqrn().length();
162 if (strncmp(ln_buf, ctx->mount_point()->fqrn().c_str(), len) == 0
163 && (ln_buf[len] == '/' || ln_buf[len] == '\0')) {
164 buf = ln_buf + len;
165 if (ln_buf[len] == '\0') {
166 buf += "/";
167 }
168 } else {
169 LogCvmfs(kLogCvmfs, kLogDebug,
170 "libcvmfs can't resolve symlinks to paths outside of the repo: "
171 "%s --> %s (mountpoint=%s)",
172 path, ln_buf, ctx->mount_point()->fqrn().c_str());
173 errno = ENOENT;
174 return -1;
175 }
176 } else {
177 // symlink is relative path
178 buf = GetParentPath(buf);
179 buf += "/";
180 buf += ln_buf;
181 }
182
183 // In case the symlink references other symlinks or contains ".."
184 // or "." we must now call expand_path on the result.
185
186 return expand_path(depth + 1, ctx, buf.c_str(), expanded_path);
187 770 }
188
189 /**
190 * Like expand_path(), but do not expand the final element of the path.
191 */
192 105 static int expand_ppath(LibContext *ctx,
193 const char *path,
194 string *expanded_path) {
195
2/4
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 105 times.
✗ Branch 6 not taken.
210 const string p_path = GetParentPath(path);
196
2/4
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 105 times.
✗ Branch 6 not taken.
210 const string fname = GetFileName(path);
197
198
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
105 if (p_path == "") {
199 *expanded_path = path;
200 return 0;
201 }
202
203
1/2
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
105 const int rc = expand_path(0, ctx, p_path.c_str(), expanded_path);
204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (rc != 0) {
205 return rc;
206 }
207
208
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 (*expanded_path) += "/";
209
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 (*expanded_path) += fname;
210
211 105 return 0;
212 105 }
213
214
215 int cvmfs_open(LibContext *ctx, const char *path) {
216 string lpath;
217 int rc;
218 rc = expand_path(0, ctx, path, &lpath);
219 if (rc < 0) {
220 return -1;
221 }
222 path = lpath.c_str();
223
224 rc = ctx->Open(path);
225 if (rc < 0) {
226 errno = -rc;
227 return -1;
228 }
229 return rc;
230 }
231
232
233 ssize_t cvmfs_pread(LibContext *ctx, int fd, void *buf, size_t size,
234 off_t off) {
235 const ssize_t nbytes = ctx->Pread(fd, buf, size, off);
236 if (nbytes < 0) {
237 errno = -nbytes;
238 return -1;
239 }
240 return nbytes;
241 }
242
243
244 int cvmfs_close(LibContext *ctx, int fd) {
245 const int rc = ctx->Close(fd);
246 if (rc < 0) {
247 errno = -rc;
248 return -1;
249 }
250 return 0;
251 }
252
253
254 int cvmfs_readlink(LibContext *ctx, const char *path, char *buf, size_t size) {
255 string lpath;
256 int rc;
257 rc = expand_ppath(ctx, path, &lpath);
258 if (rc < 0) {
259 return -1;
260 }
261 path = lpath.c_str();
262
263 rc = ctx->Readlink(path, buf, size);
264 if (rc < 0) {
265 errno = -rc;
266 return -1;
267 }
268 return 0;
269 }
270
271
272 105 int cvmfs_stat(LibContext *ctx, const char *path, struct stat *st) {
273 105 string lpath;
274 int rc;
275
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 rc = expand_path(0, ctx, path, &lpath);
276
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 70 times.
105 if (rc < 0) {
277 35 return -1;
278 }
279 70 path = lpath.c_str();
280
281
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 rc = ctx->GetAttr(path, st);
282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (rc < 0) {
283 errno = -rc;
284 return -1;
285 }
286 70 return 0;
287 105 }
288
289
290 int cvmfs_lstat(LibContext *ctx, const char *path, struct stat *st) {
291 string lpath;
292 int rc;
293 rc = expand_ppath(ctx, path, &lpath);
294 if (rc < 0) {
295 return -1;
296 }
297 path = lpath.c_str();
298
299 rc = ctx->GetAttr(path, st);
300 if (rc < 0) {
301 errno = -rc;
302 return -1;
303 }
304 return 0;
305 }
306
307
308 105 int cvmfs_stat_attr(LibContext *ctx,
309 const char *path,
310 struct cvmfs_attr *attr) {
311 105 string lpath;
312 int rc;
313
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 rc = expand_ppath(ctx, path, &lpath);
314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (rc < 0) {
315 return -1;
316 }
317 105 path = lpath.c_str();
318
319
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 rc = ctx->GetExtAttr(path, attr);
320
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 70 times.
105 if (rc < 0) {
321 35 errno = -rc;
322 35 return -1;
323 }
324 70 return 0;
325 105 }
326
327
328 35 int cvmfs_listdir(LibContext *ctx,
329 const char *path,
330 char ***buf,
331 size_t *buflen) {
332 35 string lpath;
333 int rc;
334
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 rc = expand_path(0, ctx, path, &lpath);
335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (rc < 0) {
336 return -1;
337 }
338 35 path = lpath.c_str();
339
340 35 size_t listsize = 0;
341
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 rc = ctx->ListDirectory(path, buf, &listsize, buflen, true);
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (rc < 0) {
343 errno = -rc;
344 return -1;
345 }
346 35 return 0;
347 35 }
348
349 int cvmfs_listdir_contents(LibContext *ctx,
350 const char *path,
351 char ***buf,
352 size_t *listlen,
353 size_t *buflen) {
354 string lpath;
355 int rc;
356 rc = expand_path(0, ctx, path, &lpath);
357 if (rc < 0) {
358 return -1;
359 }
360 path = lpath.c_str();
361
362 rc = ctx->ListDirectory(path, buf, listlen, buflen, false);
363 if (rc < 0) {
364 errno = -rc;
365 return -1;
366 }
367 return 0;
368 }
369
370 35 int cvmfs_listdir_stat(LibContext *ctx,
371 const char *path,
372 struct cvmfs_stat_t **buf,
373 size_t *listlen,
374 size_t *buflen) {
375 35 string lpath;
376 int rc;
377
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 rc = expand_path(0, ctx, path, &lpath);
378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (rc < 0) {
379 return -1;
380 }
381 35 path = lpath.c_str();
382
383
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 rc = ctx->ListDirectoryStat(path, buf, listlen, buflen);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (rc < 0) {
385 errno = -rc;
386 return -1;
387 }
388 35 return 0;
389 35 }
390
391
392 105 int cvmfs_stat_nc(LibContext *ctx,
393 const char *path,
394 struct cvmfs_nc_attr *nc_attr) {
395 105 string lpath;
396 int rc;
397
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 rc = expand_path(0, ctx, path, &lpath);
398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (rc < 0) {
399 return -1;
400 }
401 105 path = lpath.c_str();
402
403
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 rc = ctx->GetNestedCatalogAttr(path, nc_attr);
404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (rc < 0) {
405 errno = -rc;
406 return -1;
407 }
408 105 return 0;
409 105 }
410
411
412 70 int cvmfs_list_nc(LibContext *ctx,
413 const char *path,
414 char ***buf,
415 size_t *buflen) {
416 70 string lpath;
417 int rc;
418
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 rc = expand_path(0, ctx, path, &lpath);
419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (rc < 0) {
420 return -1;
421 }
422 70 path = lpath.c_str();
423
424
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 rc = ctx->ListNestedCatalogs(path, buf, buflen);
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (rc < 0) {
426 errno = -rc;
427 return -1;
428 }
429 70 return 0;
430 70 }
431
432
433 105 void cvmfs_list_free(char **buf) {
434 // Quick return if base pointer is NULL
435
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (!buf)
436 return;
437 105 size_t pos = 0;
438 // Iterate over each non-null entry and free
439 // This assumes no null entries, which don't currently exist
440
2/2
✓ Branch 0 taken 455 times.
✓ Branch 1 taken 105 times.
560 while (buf[pos]) {
441 455 free(buf[pos]);
442 455 pos++;
443 }
444 105 free(buf);
445 }
446
447 void cvmfs_enable_threaded(LibContext *ctx) { ctx->EnableMultiThreaded(); }
448
449 281 cvmfs_errors cvmfs_attach_repo_v2(const char *fqrn,
450 SimpleOptionsParser *opts,
451 LibContext **ctx) {
452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 281 times.
281 assert(ctx != NULL);
453
4/8
✓ Branch 2 taken 281 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 281 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 281 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 281 times.
✗ Branch 12 not taken.
281 opts->SwitchTemplateManager(new DefaultOptionsTemplateManager(fqrn));
454
2/4
✓ Branch 2 taken 281 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 281 times.
✗ Branch 6 not taken.
281 *ctx = LibContext::Create(fqrn, opts);
455
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 281 times.
281 assert(*ctx != NULL);
456 281 const loader::Failures result = (*ctx)->mount_point()->boot_status();
457
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 211 times.
281 if (result != loader::kFailOk) {
458
1/4
✓ Branch 2 taken 70 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
70 LogCvmfs(kLogCvmfs, kLogStderr, "Attaching %s failed: %s (%d)", fqrn,
459 140 (*ctx)->mount_point()->boot_error().c_str(), result);
460
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 delete *ctx;
461 70 *ctx = NULL;
462 }
463 281 return static_cast<cvmfs_errors>(result);
464 }
465
466
467 void cvmfs_adopt_options(cvmfs_context *ctx, SimpleOptionsParser *opts) {
468 ctx->set_options_mgr(opts);
469 }
470
471
472
1/2
✓ Branch 0 taken 211 times.
✗ Branch 1 not taken.
211 void cvmfs_detach_repo(LibContext *ctx) { delete ctx; }
473
474
475 351 cvmfs_errors cvmfs_init_v2(SimpleOptionsParser *opts) {
476 351 const int result = LibGlobals::Initialize(opts);
477
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 316 times.
351 if (result != loader::kFailOk) {
478
1/4
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
35 LogCvmfs(kLogCvmfs, kLogStderr, "Initialization failed: %s (%d)",
479 70 LibGlobals::GetInstance()->file_system()->boot_error().c_str(),
480 result);
481 35 LibGlobals::CleanupInstance();
482 }
483 351 return static_cast<cvmfs_errors>(result);
484 }
485
486
487 457 void cvmfs_fini() { LibGlobals::CleanupInstance(); }
488
489
490 static void (*ext_log_fn)(const char *msg) = NULL;
491
492
493 219319 static void libcvmfs_log_fn(const LogSource /*source*/,
494 const int /*mask*/,
495 const char *msg) {
496
1/2
✓ Branch 0 taken 219319 times.
✗ Branch 1 not taken.
219319 if (ext_log_fn) {
497 219319 (*ext_log_fn)(msg);
498 }
499 219319 }
500
501
502 981 void cvmfs_set_log_fn(void (*log_fn)(const char *msg)) {
503 981 ext_log_fn = log_fn;
504
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 491 times.
981 if (log_fn == NULL) {
505 490 SetAltLogFunc(NULL);
506 } else {
507 491 SetAltLogFunc(libcvmfs_log_fn);
508 }
509 981 }
510
511
512 char *cvmfs_statistics_format(cvmfs_context *ctx) {
513 assert(ctx != NULL);
514 const std::string stats = ctx->mount_point()->statistics()->PrintList(
515 perf::Statistics::kPrintHeader);
516 return strdup(stats.c_str());
517 }
518
519
520 2 int cvmfs_remount(LibContext *ctx) {
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(ctx != NULL);
522 2 return ctx->Remount();
523 }
524
525
526 3 uint64_t cvmfs_get_revision(LibContext *ctx) {
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 assert(ctx != NULL);
528 3 return ctx->GetRevision();
529 }
530