GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/libcvmfs.cc Lines: 135 256 52.7 %
Date: 2019-02-03 02:48:13 Branches: 43 118 36.4 %

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 "logging.h"
28
#include "smalloc.h"
29
#include "statistics.h"
30
#include "util/posix.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
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
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
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
21
static int expand_path(
90
  const int depth,
91
  LibContext *ctx,
92
  char const *path,
93
  string *expanded_path)
94
{
95
21
  string p_path = GetParentPath(path);
96
21
  string fname = GetFileName(path);
97
  int rc;
98
99
21
  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
21
  string buf;
120
21
  if (p_path != "") {
121
9
    rc = expand_path(depth, ctx, p_path.c_str(), &buf);
122
9
    if (rc != 0) {
123
      return -1;
124
    }
125
126
9
    if (fname == ".") {
127
      *expanded_path = buf;
128
      return 0;
129
    }
130
  }
131
132

21
  if (buf.length() == 0 || buf[buf.length()-1] != '/') {
133
21
    buf += "/";
134
  }
135
21
  buf += fname;
136
137
  struct stat st;
138
21
  rc = ctx->GetAttr(buf.c_str(), &st);
139
21
  if (rc != 0) {
140
1
    errno = -rc;
141
1
    return -1;
142
  }
143
144
20
  if (!S_ISLNK(st.st_mode)) {
145
20
    *expanded_path = buf;
146
20
    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
}
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
3
  string p_path = GetParentPath(path);
208
3
  string fname = GetFileName(path);
209
210
3
  if (p_path == "") {
211
    *expanded_path = path;
212
    return 0;
213
  }
214
215
3
  int rc = expand_path(0, ctx, p_path.c_str(), expanded_path);
216
3
  if (rc != 0) {
217
    return rc;
218
  }
219
220
3
  (*expanded_path) += "/";
221
3
  (*expanded_path) += fname;
222
223
3
  return 0;
224
}
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
3
  rc = expand_path(0, ctx, path, &lpath);
299
3
  if (rc < 0) {
300
1
    return -1;
301
  }
302
2
  path = lpath.c_str();
303
304
2
  rc = ctx->GetAttr(path, st);
305
2
  if (rc < 0) {
306
    errno = -rc;
307
    return -1;
308
  }
309
2
  return 0;
310
}
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
3
  rc = expand_ppath(ctx, path, &lpath);
339
3
  if (rc < 0) {
340
    return -1;
341
  }
342
3
  path = lpath.c_str();
343
344
3
  rc = ctx->GetExtAttr(path, attr);
345
3
  if (rc < 0) {
346
1
    errno = -rc;
347
1
    return -1;
348
  }
349
2
  return 0;
350
}
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
  rc = expand_path(0, ctx, path, &lpath);
362
1
  if (rc < 0) {
363
    return -1;
364
  }
365
1
  path = lpath.c_str();
366
367
1
  size_t listsize = 0;
368
1
  rc = ctx->ListDirectory(path, buf, &listsize, buflen, true);
369
1
  if (rc < 0) {
370
    errno = -rc;
371
    return -1;
372
  }
373
1
  return 0;
374
}
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
400
401
3
int cvmfs_stat_nc(
402
  LibContext *ctx,
403
  const char *path,
404
  struct cvmfs_nc_attr *nc_attr
405
) {
406
3
  string lpath;
407
  int rc;
408
3
  rc = expand_path(0, ctx, path, &lpath);
409
3
  if (rc < 0) {
410
    return -1;
411
  }
412
3
  path = lpath.c_str();
413
414
3
  rc = ctx->GetNestedCatalogAttr(path, nc_attr);
415
3
  if (rc < 0) {
416
    errno = -rc;
417
    return -1;
418
  }
419
3
  return 0;
420
}
421
422
423
2
int cvmfs_list_nc(
424
  LibContext *ctx,
425
  const char *path,
426
  char ***buf,
427
  size_t *buflen
428
) {
429
2
  string lpath;
430
  int rc;
431
2
  rc = expand_path(0, ctx, path, &lpath);
432
2
  if (rc < 0) {
433
    return -1;
434
  }
435
2
  path = lpath.c_str();
436
437
2
  rc = ctx->ListNestedCatalogs(path, buf, buflen);
438
2
  if (rc < 0) {
439
    errno = -rc;
440
    return -1;
441
  }
442
2
  return 0;
443
}
444
445
446
3
void cvmfs_list_free(char **buf)
447
{
448
  // Quick return if base pointer is NULL
449
3
  if (!buf) return;
450
3
  size_t pos = 0;
451
  // Iterate over each non-null entry and free
452
  // This assumes no null entries, which don't currently exist
453
19
  while (buf[pos]) {
454
13
    free(buf[pos]);
455
13
    pos++;
456
  }
457
3
  free(buf);
458
}
459
460
void cvmfs_enable_threaded(
461
  LibContext *ctx
462
) {
463
  ctx->EnableMultiThreaded();
464
}
465
466
8
cvmfs_errors cvmfs_attach_repo_v2(
467
  const char *fqrn,
468
  SimpleOptionsParser *opts,
469
  LibContext **ctx)
470
{
471
8
  assert(ctx != NULL);
472
8
  opts->SwitchTemplateManager(new DefaultOptionsTemplateManager(fqrn));
473
8
  *ctx = LibContext::Create(fqrn, opts);
474
8
  assert(*ctx != NULL);
475
8
  loader::Failures result = (*ctx)->mount_point()->boot_status();
476
8
  if (result != loader::kFailOk) {
477
    LogCvmfs(kLogCvmfs, kLogStderr, "Attaching %s failed: %s (%d)",
478
3
             fqrn, (*ctx)->mount_point()->boot_error().c_str(), result);
479
3
    delete *ctx;
480
3
    *ctx = NULL;
481
  }
482
8
  return static_cast<cvmfs_errors>(result);
483
}
484
485
486
void cvmfs_adopt_options(cvmfs_context *ctx, SimpleOptionsParser *opts) {
487
  ctx->set_options_mgr(opts);
488
}
489
490
491
5
void cvmfs_detach_repo(LibContext *ctx) {
492
5
  delete ctx;
493
5
}
494
495
496
10
cvmfs_errors cvmfs_init_v2(SimpleOptionsParser *opts) {
497
10
  int result = LibGlobals::Initialize(opts);
498
10
  if (result != loader::kFailOk) {
499
    LogCvmfs(kLogCvmfs, kLogStderr, "Initialization failed: %s (%d)",
500
             LibGlobals::GetInstance()->file_system()->boot_error().c_str(),
501
1
             result);
502
1
    LibGlobals::CleanupInstance();
503
  }
504
10
  return static_cast<cvmfs_errors>(result);
505
}
506
507
508
15
void cvmfs_fini() {
509
15
  LibGlobals::CleanupInstance();
510
15
}
511
512
513
static void (*ext_log_fn)(const char *msg) = NULL;
514
515
516
6922
static void libcvmfs_log_fn(
517
  const LogSource /*source*/,
518
  const int /*mask*/,
519
  const char *msg
520
) {
521
6922
  if (ext_log_fn) {
522
6922
    (*ext_log_fn)(msg);
523
  }
524
6922
}
525
526
527
29
void cvmfs_set_log_fn(void (*log_fn)(const char *msg))
528
{
529
29
  ext_log_fn = log_fn;
530
29
  if (log_fn == NULL) {
531
14
    SetAltLogFunc(NULL);
532
  } else {
533
15
    SetAltLogFunc(libcvmfs_log_fn);
534
  }
535
29
}
536
537
538
char *cvmfs_statistics_format(cvmfs_context *ctx) {
539
  assert(ctx != NULL);
540
  std::string stats = ctx->mount_point()->statistics()
541
    ->PrintList(perf::Statistics::kPrintHeader);
542
  return strdup(stats.c_str());
543
}
544
545
546
int cvmfs_remount(LibContext *ctx) {
547
  // not implemented
548
  return -1;
549
}