GCC Code Coverage Report


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