GCC Code Coverage Report


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