GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/libcvmfs.cc
Date: 2025-06-22 02:36:02
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 144 struct cvmfs_attr *cvmfs_attr_init() {
40 struct cvmfs_attr *attr;
41 144 attr = reinterpret_cast<cvmfs_attr *>(calloc(1, sizeof(*attr)));
42 144 attr->version = 1;
43 144 attr->size = sizeof(*attr);
44 144 return attr;
45 }
46
47
48 /**
49 * Destroy the cvmfs_attr struct and frees the checksum, symlink,
50 * name, and xattrs.
51 */
52 144 void cvmfs_attr_free(struct cvmfs_attr *attr) {
53
1/2
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
144 if (attr) {
54 144 free(attr->cvm_checksum);
55 144 free(attr->cvm_symlink);
56 144 free(attr->cvm_name);
57 144 free(attr->cvm_parent);
58
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 delete reinterpret_cast<XattrList *>(attr->cvm_xattrs);
59 }
60 144 free(attr);
61 144 }
62
63
64 147 struct cvmfs_nc_attr *cvmfs_nc_attr_init() {
65 struct cvmfs_nc_attr *attr;
66 147 attr = reinterpret_cast<cvmfs_nc_attr *>(calloc(1, sizeof(*attr)));
67 147 return attr;
68 }
69
70 147 void cvmfs_nc_attr_free(struct cvmfs_nc_attr *nc_attr) {
71
1/2
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
147 if (nc_attr) {
72 147 free(nc_attr->mountpoint);
73 147 free(nc_attr->hash);
74 }
75 147 free(nc_attr);
76 147 }
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 1063 static int expand_path(const int depth,
86 LibContext *ctx,
87 char const *path,
88 string *expanded_path) {
89
2/4
✓ Branch 2 taken 1063 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1063 times.
✗ Branch 6 not taken.
2126 const string p_path = GetParentPath(path);
90
2/4
✓ Branch 2 taken 1063 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1063 times.
✗ Branch 6 not taken.
2126 const string fname = GetFileName(path);
91 int rc;
92
93
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1063 times.
1063 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 1063 string buf;
115
2/2
✓ Branch 1 taken 436 times.
✓ Branch 2 taken 627 times.
1063 if (p_path != "") {
116
1/2
✓ Branch 2 taken 436 times.
✗ Branch 3 not taken.
436 rc = expand_path(depth, ctx, p_path.c_str(), &buf);
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 436 times.
436 if (rc != 0) {
118 return -1;
119 }
120
121
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 436 times.
436 if (fname == ".") {
122 *expanded_path = buf;
123 return 0;
124 }
125 }
126
127
5/8
✓ Branch 1 taken 436 times.
✓ Branch 2 taken 627 times.
✓ Branch 5 taken 436 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 436 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1063 times.
✗ Branch 10 not taken.
1063 if (buf.length() == 0 || buf[buf.length() - 1] != '/') {
128
1/2
✓ Branch 1 taken 1063 times.
✗ Branch 2 not taken.
1063 buf += "/";
129 }
130
1/2
✓ Branch 1 taken 1063 times.
✗ Branch 2 not taken.
1063 buf += fname;
131
132 struct stat st;
133
1/2
✓ Branch 2 taken 1063 times.
✗ Branch 3 not taken.
1063 rc = ctx->GetAttr(buf.c_str(), &st);
134
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 1015 times.
1063 if (rc != 0) {
135 48 errno = -rc;
136 48 return -1;
137 }
138
139
1/2
✓ Branch 0 taken 1015 times.
✗ Branch 1 not taken.
1015 if (!S_ISLNK(st.st_mode)) {
140
1/2
✓ Branch 1 taken 1015 times.
✗ Branch 2 not taken.
1015 *expanded_path = buf;
141 1015 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 1063 }
193
194 /**
195 * Like expand_path(), but do not expand the final element of the path.
196 */
197 144 static int expand_ppath(LibContext *ctx,
198 const char *path,
199 string *expanded_path) {
200
2/4
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✗ Branch 6 not taken.
288 const string p_path = GetParentPath(path);
201
2/4
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✗ Branch 6 not taken.
288 const string fname = GetFileName(path);
202
203
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
144 if (p_path == "") {
204 *expanded_path = path;
205 return 0;
206 }
207
208
1/2
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
144 const int rc = expand_path(0, ctx, p_path.c_str(), expanded_path);
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (rc != 0) {
210 return rc;
211 }
212
213
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 (*expanded_path) += "/";
214
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 (*expanded_path) += fname;
215
216 144 return 0;
217 144 }
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(
239 LibContext *ctx, int fd, void *buf, size_t size, 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 144 int cvmfs_stat(LibContext *ctx, const char *path, struct stat *st) {
278 144 string lpath;
279 int rc;
280
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 rc = expand_path(0, ctx, path, &lpath);
281
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 96 times.
144 if (rc < 0) {
282 48 return -1;
283 }
284 96 path = lpath.c_str();
285
286
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 rc = ctx->GetAttr(path, st);
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (rc < 0) {
288 errno = -rc;
289 return -1;
290 }
291 96 return 0;
292 144 }
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 144 int cvmfs_stat_attr(LibContext *ctx,
314 const char *path,
315 struct cvmfs_attr *attr) {
316 144 string lpath;
317 int rc;
318
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 rc = expand_ppath(ctx, path, &lpath);
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (rc < 0) {
320 return -1;
321 }
322 144 path = lpath.c_str();
323
324
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 rc = ctx->GetExtAttr(path, attr);
325
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 96 times.
144 if (rc < 0) {
326 48 errno = -rc;
327 48 return -1;
328 }
329 96 return 0;
330 144 }
331
332
333 48 int cvmfs_listdir(LibContext *ctx,
334 const char *path,
335 char ***buf,
336 size_t *buflen) {
337 48 string lpath;
338 int rc;
339
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 rc = expand_path(0, ctx, path, &lpath);
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if (rc < 0) {
341 return -1;
342 }
343 48 path = lpath.c_str();
344
345 48 size_t listsize = 0;
346
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 rc = ctx->ListDirectory(path, buf, &listsize, buflen, true);
347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if (rc < 0) {
348 errno = -rc;
349 return -1;
350 }
351 48 return 0;
352 48 }
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 48 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 48 string lpath;
381 int rc;
382
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 rc = expand_path(0, ctx, path, &lpath);
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if (rc < 0) {
384 return -1;
385 }
386 48 path = lpath.c_str();
387
388
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 rc = ctx->ListDirectoryStat(path, buf, listlen, buflen);
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if (rc < 0) {
390 errno = -rc;
391 return -1;
392 }
393 48 return 0;
394 48 }
395
396
397 147 int cvmfs_stat_nc(LibContext *ctx,
398 const char *path,
399 struct cvmfs_nc_attr *nc_attr) {
400 147 string lpath;
401 int rc;
402
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 rc = expand_path(0, ctx, path, &lpath);
403
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 147 times.
147 if (rc < 0) {
404 return -1;
405 }
406 147 path = lpath.c_str();
407
408
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 rc = ctx->GetNestedCatalogAttr(path, nc_attr);
409
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 147 times.
147 if (rc < 0) {
410 errno = -rc;
411 return -1;
412 }
413 147 return 0;
414 147 }
415
416
417 96 int cvmfs_list_nc(LibContext *ctx,
418 const char *path,
419 char ***buf,
420 size_t *buflen) {
421 96 string lpath;
422 int rc;
423
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 rc = expand_path(0, ctx, path, &lpath);
424
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (rc < 0) {
425 return -1;
426 }
427 96 path = lpath.c_str();
428
429
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 rc = ctx->ListNestedCatalogs(path, buf, buflen);
430
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (rc < 0) {
431 errno = -rc;
432 return -1;
433 }
434 96 return 0;
435 96 }
436
437
438 144 void cvmfs_list_free(char **buf) {
439 // Quick return if base pointer is NULL
440
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (!buf)
441 return;
442 144 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 624 times.
✓ Branch 1 taken 144 times.
768 while (buf[pos]) {
446 624 free(buf[pos]);
447 624 pos++;
448 }
449 144 free(buf);
450 }
451
452 void cvmfs_enable_threaded(LibContext *ctx) { ctx->EnableMultiThreaded(); }
453
454 386 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 386 times.
386 assert(ctx != NULL);
458
4/8
✓ Branch 2 taken 386 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 386 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 386 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 386 times.
✗ Branch 12 not taken.
386 opts->SwitchTemplateManager(new DefaultOptionsTemplateManager(fqrn));
459
2/4
✓ Branch 2 taken 386 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 386 times.
✗ Branch 6 not taken.
386 *ctx = LibContext::Create(fqrn, opts);
460
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 386 times.
386 assert(*ctx != NULL);
461 386 const loader::Failures result = (*ctx)->mount_point()->boot_status();
462
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 290 times.
386 if (result != loader::kFailOk) {
463
1/4
✓ Branch 2 taken 96 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
96 LogCvmfs(kLogCvmfs, kLogStderr, "Attaching %s failed: %s (%d)", fqrn,
464 192 (*ctx)->mount_point()->boot_error().c_str(), result);
465
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 delete *ctx;
466 96 *ctx = NULL;
467 }
468 386 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 290 times.
✗ Branch 1 not taken.
290 void cvmfs_detach_repo(LibContext *ctx) { delete ctx; }
478
479
480 484 cvmfs_errors cvmfs_init_v2(SimpleOptionsParser *opts) {
481 484 const int result = LibGlobals::Initialize(opts);
482
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 435 times.
484 if (result != loader::kFailOk) {
483
1/4
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
49 LogCvmfs(kLogCvmfs, kLogStderr, "Initialization failed: %s (%d)",
484 98 LibGlobals::GetInstance()->file_system()->boot_error().c_str(),
485 result);
486 49 LibGlobals::CleanupInstance();
487 }
488 484 return static_cast<cvmfs_errors>(result);
489 }
490
491
492 628 void cvmfs_fini() { LibGlobals::CleanupInstance(); }
493
494
495 static void (*ext_log_fn)(const char *msg) = NULL;
496
497
498 301992 static void libcvmfs_log_fn(const LogSource /*source*/,
499 const int /*mask*/,
500 const char *msg) {
501
1/2
✓ Branch 0 taken 301992 times.
✗ Branch 1 not taken.
301992 if (ext_log_fn) {
502 301992 (*ext_log_fn)(msg);
503 }
504 301992 }
505
506
507 1349 void cvmfs_set_log_fn(void (*log_fn)(const char *msg)) {
508 1349 ext_log_fn = log_fn;
509
2/2
✓ Branch 0 taken 674 times.
✓ Branch 1 taken 675 times.
1349 if (log_fn == NULL) {
510 674 SetAltLogFunc(NULL);
511 } else {
512 675 SetAltLogFunc(libcvmfs_log_fn);
513 }
514 1349 }
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