GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/libcvmfs_legacy.cc Lines: 131 220 59.5 %
Date: 2019-02-03 02:48:13 Branches: 117 254 46.1 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 *
4
 * Translates cvmfs_init and cvmfs_repo_attach calls into their contemporary
5
 * counterparts.
6
 */
7
8
#include "cvmfs_config.h"
9
#include "libcvmfs.h"
10
11
#include <cstdio>
12
#include <cstring>
13
#include <string>
14
15
#include "libcvmfs_int.h"
16
#include "loader.h"
17
#include "options.h"
18
#include "util/string.h"
19
20
using namespace std;  // NOLINT
21
22
17
static int set_option(char const *name, char const *value, bool *var) {
23
17
  if (*value != '\0') {
24
    fprintf(stderr, "Option %s=%s contains a value when none was expected.\n",
25
            name, value);
26
    return -1;
27
  }
28
17
  *var = true;
29
17
  return 0;
30
}
31
32
33
22
static int set_option(char const *name, char const *value, unsigned *var) {
34
22
  unsigned v = 0;
35
22
  int end = 0;
36
22
  int rc = sscanf(value, "%u%n", &v, &end);
37

22
  if (rc != 1 || value[end] != '\0') {
38
    fprintf(stderr, "Invalid unsigned integer value for %s=%s\n", name, value);
39
    return -1;
40
  }
41
22
  *var = v;
42
22
  return 0;
43
}
44
45
46
13
static int set_option(char const *name, char const *value, int *var) {
47
13
  int v = 0;
48
13
  int end = 0;
49
13
  int rc = sscanf(value, "%d%n", &v, &end);
50

13
  if (rc != 1 || value[end] != '\0') {
51
    fprintf(stderr, "Invalid integer value for %s=%s\n", name, value);
52
    return -1;
53
  }
54
13
  *var = v;
55
13
  return 0;
56
}
57
58
59
27
static int set_option(char const *name, char const *value, string *var) {
60
27
  *var = value;
61
27
  return 0;
62
}
63
64
65
#define CVMFS_OPT(var) if (strcmp(name, #var) == 0) \
66
  return ::set_option(name, value, &var)
67
68
69
10
struct cvmfs_repo_options {
70
  int set_option(char const *name, char const *value) {
71
    CVMFS_OPT(allow_unsigned);
72
    CVMFS_OPT(blacklist);
73
    CVMFS_OPT(deep_mount);  // deprecated
74
    CVMFS_OPT(fallback_proxies);
75
    CVMFS_OPT(mountpoint);
76
    CVMFS_OPT(proxies);
77
    CVMFS_OPT(pubkey);
78
    CVMFS_OPT(repo_name);
79
    CVMFS_OPT(timeout);
80
    CVMFS_OPT(timeout_direct);
81
    CVMFS_OPT(tracefile);
82
    CVMFS_OPT(url);
83
84
    fprintf(stderr, "Unknown repo option: %s\n", name);
85
    return -1;
86
  }
87
88
  int verify_sanity() {
89
    if (mountpoint.empty() && !repo_name.empty()) {
90
      mountpoint = "/cvmfs/";
91
      mountpoint += repo_name;
92
    }
93
    while (mountpoint.length() > 0 && mountpoint[mountpoint.length()-1] == '/')
94
    {
95
      mountpoint.resize(mountpoint.length()-1);
96
    }
97
98
    return LIBCVMFS_FAIL_OK;
99
  }
100
101
10
  cvmfs_repo_options() :
102
    timeout(2),
103
    timeout_direct(2),
104
    pubkey("/etc/cvmfs/keys/cern.ch.pub"),
105
    blacklist(""),
106
10
    allow_unsigned(false) {}
107
108
  unsigned       timeout;
109
  unsigned       timeout_direct;
110
  std::string    url;
111
  std::string    external_url;
112
  std::string    proxies;
113
  std::string    fallback_proxies;
114
  std::string    tracefile;  // unused
115
  std::string    pubkey;
116
  std::string    deep_mount;
117
  std::string    blacklist;
118
  std::string    repo_name;
119
  std::string    root_hash;
120
  std::string    mountpoint;
121
  bool           allow_unsigned;
122
};
123
124
125
17
struct cvmfs_global_options {
126
80
  int set_option(char const *name, char const *value) {
127
80
    CVMFS_OPT(alien_cache);
128
78
    CVMFS_OPT(alien_cachedir);
129
76
    CVMFS_OPT(cache_directory);
130
61
    CVMFS_OPT(cachedir);
131
59
    CVMFS_OPT(lock_directory);
132
57
    CVMFS_OPT(change_to_cache_directory);
133
53
    CVMFS_OPT(logfile);
134
52
    CVMFS_OPT(log_file);
135
51
    CVMFS_OPT(log_prefix);
136
47
    CVMFS_OPT(log_syslog_level);
137
46
    CVMFS_OPT(syslog_level);
138
45
    CVMFS_OPT(max_open_files);
139
37
    CVMFS_OPT(nofiles);
140
34
    CVMFS_OPT(quota_limit);
141
23
    CVMFS_OPT(quota_threshold);
142
12
    CVMFS_OPT(rebuild_cachedb);
143
144
1
    fprintf(stderr, "Unknown global option: %s\n", name);
145
1
    return LIBCVMFS_FAIL_BADOPT;
146
  }
147
148
16
  int verify_sanity() {
149
    // Alias handling
150

16
    if ((nofiles >= 0) && (max_open_files != 0) && (nofiles != max_open_files))
151
1
      return LIBCVMFS_FAIL_BADOPT;
152
15
    if (nofiles >= 0)
153
2
      max_open_files = nofiles;
154
155

15
    if ((syslog_level >= 0) && (log_syslog_level != 0) &&
156
        (syslog_level != log_syslog_level))
157
    {
158
1
      return LIBCVMFS_FAIL_BADOPT;
159
    }
160
14
    if (syslog_level >= 0)
161
      log_syslog_level = syslog_level;
162
14
    if (log_syslog_level < 0)
163
14
      log_syslog_level = 3;
164
165


14
    if ((logfile != "") && (log_file != "") && (log_file != logfile))
166
1
      return LIBCVMFS_FAIL_BADOPT;
167
13
    if (logfile != "")
168
      log_file = logfile;
169
170


13
    if ((cachedir != "") && (cache_directory != "") &&
171
        (cache_directory != cachedir))
172
    {
173
1
      return LIBCVMFS_FAIL_BADOPT;
174
    }
175
12
    if (cachedir != "")
176
1
      cache_directory = cachedir;
177
178
12
    return LIBCVMFS_FAIL_OK;
179
  }
180
181
17
  cvmfs_global_options()
182
    : change_to_cache_directory(false)
183
    , alien_cache(false)
184
    , syslog_level(-1)
185
    , log_syslog_level(-1)
186
    , nofiles(-1)
187
    , max_open_files(0)
188
    , quota_limit(0)
189
    , quota_threshold(0)
190
17
    , rebuild_cachedb(0)
191
17
  { }
192
193
  std::string    cache_directory;
194
  std::string    cachedir;  // Alias of cache_directory
195
  std::string    alien_cachedir;
196
  std::string    lock_directory;
197
  bool           change_to_cache_directory;
198
  bool           alien_cache;
199
200
  int            syslog_level;
201
  int            log_syslog_level;
202
  std::string    log_prefix;
203
  std::string    logfile;
204
  std::string    log_file;
205
206
  int            nofiles;
207
  int            max_open_files;  // Alias of nofiles
208
209
  // Currently ignored
210
  unsigned quota_limit;
211
  unsigned quota_threshold;
212
  bool rebuild_cachedb;
213
};
214
215
216
/**
217
 * Structure to parse the file system options.
218
 */
219
template <class DerivedT>
220
34
struct cvmfs_options : public DerivedT {
221
80
  int set_option(char const *name, char const *value) {
222
80
    return DerivedT::set_option(name, value);
223
  }
224
225
17
  int parse_options(char const *options)
226
  {
227



17
    while (*options) {
228
80
      char const *next = options;
229
80
      string name;
230
80
      string value;
231
232
      // get the option name
233




1176
      for (next=options; *next && *next != ',' && *next != '='; next++) {
234

1096
        if (*next == '\\') {
235
          next++;
236
          if (*next == '\0') break;
237
        }
238
1096
        name += *next;
239
      }
240
241

80
      if (*next == '=') {
242
62
        next++;
243
      }
244
245
      // get the option value
246



765
      for (; *next && *next != ','; next++) {
247

685
        if (*next == '\\') {
248
          next++;
249
          if (*next == '\0') break;
250
        }
251
685
        value += *next;
252
      }
253
254



80
      if (!name.empty() || !value.empty()) {
255
80
        int result = set_option(name.c_str(), value.c_str());
256

80
        if (result != 0) {
257
1
          return result;
258
        }
259
      }
260
261

79
      if (*next == ',') next++;
262
79
      options = next;
263
    }
264
265
16
    return DerivedT::verify_sanity();
266
  }
267
};
268
269
typedef cvmfs_options<cvmfs_repo_options>   repo_options;
270
typedef cvmfs_options<cvmfs_global_options> global_options;
271
272
/**
273
 * Display the usage message.
274
 */
275
10
static void usage() {
276
10
  struct cvmfs_repo_options defaults;
277
  fprintf(stderr,
278
  "CernVM-FS version %s\n"
279
  "Copyright (c) 2009- CERN\n"
280
  "All rights reserved\n\n"
281
  "Please visit http://cernvm.cern.ch/project/info for license details "
282
  "and author list.\n\n"
283
284
  "libcvmfs options are expected in the form: option1,option2,option3,...\n"
285
  "Within an option, the characters , and \\ must be preceded by \\.\n\n"
286
287
  "There are two types of options (global and repository specifics)\n"
288
  "  cvmfs_init()        expects global options\n"
289
  "  cvmfs_attach_repo() expects repository specific options\n"
290
291
  "global options are:\n"
292
  " cache_directory/cachedir=DIR Where to store disk cache\n"
293
  " change_to_cache_directory  Performs a cd to the cache directory "
294
                               "(performance tweak)\n"
295
  " alien_cache                Treat cache directory as alien cache\n"
296
  " alien_cachedir=DIR         Explicitly set an alien cache directory\n"
297
  " lock_directory=DIR         Directory for per instance lock files.\n"
298
  "                            Needs to be on a file system with POSIX locks.\n"
299
  "                            Should be different from alien cache directory."
300
  "                            \nDefaults to cache_directory.\n"
301
  " (log_)syslog_level=LEVEL   Sets the level used for syslog to "
302
                               "DEBUG (1), INFO (2), or NOTICE (3).\n"
303
  "                            Default is NOTICE.\n"
304
  " log_prefix                 String to use as a log prefix in syslog\n"
305
  " log_file/logfile           Logs all messages to FILE instead of "
306
                               "stderr and daemonizes.\n"
307
  "                            Makes only sense for the debug version\n"
308
  " nofiles/max_open_files     Set the maximum number of open files "
309
                               "for CernVM-FS process (soft limit)\n\n"
310
311
  "repository specific options are:"
312
  " repo_name=REPO_NAME        Unique name of the mounted repository, "
313
                               "e.g. atlas.cern.ch\n"
314
  " url=REPOSITORY_URL         The URL of the CernVM-FS server(s): "
315
                               "'url1;url2;...'\n"
316
  " timeout=SECONDS            Timeout for network operations (default is %d)\n"
317
  " timeout_direct=SECONDS     Timeout for network operations without proxy "
318
                               "(default is %d)\n"
319
  " proxies=HTTP_PROXIES       Set the HTTP proxy list, such as "
320
                               "'proxy1|proxy2;DIRECT'\n"
321
  " fallback_proxies=PROXIES   Set the fallback proxy list, such as "
322
                               "'proxy1;proxy2'\n"
323
  " tracefile=FILE             Trace FUSE opaerations into FILE\n"
324
  " pubkey=PEMFILE             Public RSA key that is used to verify the "
325
                               "whitelist signature.\n"
326
  " allow_unsigned             Accept unsigned catalogs "
327
                               "(allows man-in-the-middle attacks)\n"
328
  " deep_mount=PREFIX          Path prefix if a repository is mounted on a "
329
                               "nested catalog,\n"
330
  "                            i.e. deep_mount=/software/15.0.1\n"
331
  " mountpoint=PATH            Path to root of repository, "
332
                               "e.g. /cvmfs/atlas.cern.ch\n"
333
  " blacklist=FILE             Local blacklist for invalid certificates. "
334
                               "Has precedence over the whitelist.\n",
335
10
  PACKAGE_VERSION, defaults.timeout, defaults.timeout_direct);
336
10
}
337
338
339
/**
340
 * Translates from loader::Failure codes to legacy LIBCVMFS_FAIL_... constants.
341
 */
342
12
static int TranslateReturnValue(loader::Failures code) {
343
12
  const int unknown = -10;
344
345



12
  switch (code) {
346
    case loader::kFailOk:
347
7
      return LIBCVMFS_FAIL_OK;
348
    case loader::kFailUnknown:
349
      return unknown;  // missing constant
350
    case loader::kFailOptions:
351
      return LIBCVMFS_FAIL_BADOPT;
352
    case loader::kFailPermission:
353
4
      return LIBCVMFS_FAIL_NOFILES;
354
    case loader::kFailMount:
355
    case loader::kFailLoaderTalk:
356
    case loader::kFailFuseLoop:
357
    case loader::kFailLoadLibrary:
358
    case loader::kFailIncompatibleVersions:
359
      return unknown;
360
    case loader::kFailCacheDir:
361
1
      return LIBCVMFS_FAIL_MKCACHE;
362
    case loader::kFailPeers:
363
    case loader::kFailNfsMaps:
364
      return unknown;
365
    case loader::kFailQuota:
366
      return LIBCVMFS_FAIL_INITQUOTA;
367
    case loader::kFailMonitor:
368
    case loader::kFailTalk:
369
      return unknown;
370
    case loader::kFailSignature:
371
    case loader::kFailCatalog:
372
      return LIBCVMFS_FAIL_INITCACHE;
373
    case loader::kFailMaintenanceMode:
374
    case loader::kFailSaveState:
375
    case loader::kFailRestoreState:
376
    case loader::kFailOtherMount:
377
    case loader::kFailDoubleMount:
378
    case loader::kFailHistory:
379
    case loader::kFailRevisionBlacklisted:
380
      return unknown;
381
    case loader::kFailWpad:
382
      return LIBCVMFS_FAIL_INITCACHE;
383
    case loader::kFailLockWorkspace:
384
      return LIBCVMFS_FAIL_LOCKFILE;
385
    default:
386
      return unknown;
387
  }
388
}
389
390
391
17
SimpleOptionsParser *cvmfs_options_init_legacy(char const *legacy_options) {
392
17
  global_options global_opts;
393
17
  int parse_result = global_opts.parse_options(legacy_options);
394
17
  if (parse_result != 0) {
395
5
    fprintf(stderr, "Invalid CVMFS global options: %s.\n", legacy_options);
396
5
    usage();
397
5
    return NULL;
398
  }
399
400
12
  SimpleOptionsParser *options_mgr = cvmfs_options_init();
401
12
  options_mgr->SetValue("CVMFS_CACHE_DIR", global_opts.cache_directory);
402
12
  if (!global_opts.lock_directory.empty()) {
403
2
    options_mgr->SetValue("CVMFS_WORKSPACE", global_opts.lock_directory);
404
  }
405
12
  if (global_opts.alien_cache) {
406
2
    options_mgr->SetValue("CVMFS_ALIEN_CACHE", global_opts.cache_directory);
407
  }
408
12
  if (!global_opts.alien_cachedir.empty()) {
409
2
    options_mgr->SetValue("CVMFS_ALIEN_CACHE", global_opts.alien_cachedir);
410
  }
411
  // Note: as of version 2.4 CVMFS_CWD_CACHE support was dropped due to
412
  // disproportional large complexity in the FileSystem class
413
  // if (global_opts.change_to_cache_directory) {
414
  //  options_mgr->SetValue("CVMFS_CWD_CACHE", "on");
415
  // }
416
  options_mgr->SetValue("CVMFS_SYSLOG_LEVEL",
417
12
                        StringifyInt(global_opts.log_syslog_level));
418
12
  if (!global_opts.log_prefix.empty()) {
419
4
    options_mgr->SetValue("CVMFS_SYSLOG_PREFIX", global_opts.log_prefix);
420
  }
421
12
  if (!global_opts.log_file.empty()) {
422
    options_mgr->SetValue("CVMFS_DEBUGLOG", global_opts.log_file);
423
  }
424
12
  if (global_opts.max_open_files > 0) {
425
    options_mgr->SetValue("CVMFS_NFILES",
426
8
                          StringifyInt(global_opts.max_open_files));
427
  }
428
429
12
  return options_mgr;
430
}
431
432
433
17
int cvmfs_init(char const *options) {
434
17
  SimpleOptionsParser *options_mgr = cvmfs_options_init_legacy(options);
435
17
  if (options_mgr == NULL) {
436
5
    fprintf(stderr, "Invalid CVMFS global options: %s.\n", options);
437
5
    usage();
438
5
    return LIBCVMFS_FAIL_BADOPT;
439
  }
440
441
12
  loader::Failures result = LibGlobals::Initialize(options_mgr);
442
12
  LibGlobals::GetInstance()->set_options_mgr(options_mgr);
443
12
  if (result != loader::kFailOk)
444
5
    LibGlobals::CleanupInstance();
445
12
  return TranslateReturnValue(result);
446
}
447
448
449
SimpleOptionsParser *cvmfs_options_clone_legacy(
450
  SimpleOptionsParser *opts,
451
  const char *legacy_options)
452
{
453
  // Parse options
454
  repo_options repo_opts;
455
  int parse_result = repo_opts.parse_options(legacy_options);
456
  if ((parse_result != 0) || repo_opts.url.empty()) {
457
    return NULL;
458
  }
459
460
  SimpleOptionsParser *options_mgr = cvmfs_options_clone(opts);
461
  options_mgr->SwitchTemplateManager(
462
    new DefaultOptionsTemplateManager(repo_opts.repo_name));
463
  options_mgr->SetValue("CVMFS_FQRN", repo_opts.repo_name);
464
  options_mgr->SetValue("CVMFS_TIMEOUT", StringifyInt(repo_opts.timeout));
465
  options_mgr->SetValue("CVMFS_TIMEOUT_DIRECT",
466
                       StringifyInt(repo_opts.timeout_direct));
467
  options_mgr->SetValue("CVMFS_SERVER_URL", repo_opts.url);
468
  if (!repo_opts.external_url.empty()) {
469
    options_mgr->SetValue("CVMFS_EXTERNAL_URL", repo_opts.external_url);
470
  }
471
  if (repo_opts.proxies.empty()) {
472
    if (!options_mgr->IsDefined("CVMFS_HTTP_PROXY"))
473
      options_mgr->SetValue("CVMFS_HTTP_PROXY", "DIRECT");
474
  } else {
475
    options_mgr->SetValue("CVMFS_HTTP_PROXY", repo_opts.proxies);
476
  }
477
  options_mgr->SetValue("CVMFS_FALLBACK_PROXY", repo_opts.fallback_proxies);
478
  options_mgr->SetValue("CVMFS_PUBLIC_KEY", repo_opts.pubkey);
479
  if (!repo_opts.blacklist.empty()) {
480
    options_mgr->SetValue("CVMFS_BLACKLIST", repo_opts.blacklist);
481
  }
482
  if (!repo_opts.root_hash.empty()) {
483
    options_mgr->SetValue("CVMFS_ROOT_HASH", repo_opts.root_hash);
484
  }
485
486
  return options_mgr;
487
}
488
489
490
LibContext* cvmfs_attach_repo(char const *options)
491
{
492
  SimpleOptionsParser *options_mgr_base = cvmfs_options_init();
493
  SimpleOptionsParser *options_mgr =
494
    cvmfs_options_clone_legacy(options_mgr_base, options);
495
  cvmfs_options_fini(options_mgr_base);
496
  if (options_mgr == NULL) {
497
    fprintf(stderr, "Invalid CVMFS options: %s.\n", options);
498
    usage();
499
    return NULL;
500
  }
501
502
  string repo_name;
503
  bool retval = options_mgr->GetValue("CVMFS_FQRN", &repo_name);
504
  assert(retval);
505
  LibContext *ctx = LibContext::Create(repo_name, options_mgr);
506
  assert(ctx != NULL);
507
  if (ctx->mount_point()->boot_status() != loader::kFailOk) {
508
    LogCvmfs(kLogCvmfs, kLogDebug, "failed attaching %s, %s (%d)",
509
             repo_name.c_str(), ctx->mount_point()->boot_error().c_str(),
510
             ctx->mount_point()->boot_status());
511
    delete ctx;
512
    return NULL;
513
  }
514
  ctx->set_options_mgr(options_mgr);
515
  return ctx;
516
}