GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/loader.cc Lines: 0 435 0.0 %
Date: 2019-02-03 02:48:13 Branches: 0 268 0.0 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 *
4
 * Implements stub callback functions for Fuse.  Their purpose is to
5
 * redirect calls to the cvmfs shared library and to block calls during the
6
 * update of the library.
7
 *
8
 * The main executable and the cvmfs shared library _must not_ share any
9
 * symbols.
10
 */
11
12
#define ENOATTR ENODATA  /**< instead of including attr/xattr.h */
13
#define FUSE_USE_VERSION 26
14
#define _FILE_OFFSET_BITS 64
15
16
#include "cvmfs_config.h"
17
#include "loader.h"
18
19
#include <dlfcn.h>
20
#include <errno.h>
21
#include <fcntl.h>
22
#include <fuse/fuse_lowlevel.h>
23
#include <fuse/fuse_opt.h>
24
#include <openssl/crypto.h>
25
#include <sched.h>
26
#include <signal.h>
27
#include <stddef.h>
28
#include <sys/resource.h>
29
#include <time.h>
30
#include <unistd.h>
31
// If valgrind headers are present on the build system, then we can detect
32
// valgrind at runtime.
33
#ifdef HAS_VALGRIND_HEADERS
34
#include <valgrind/valgrind.h>
35
#endif
36
37
#include <cassert>
38
#include <cstdlib>
39
#include <cstring>
40
#include <string>
41
#include <vector>
42
43
#include "atomic.h"
44
#include "duplex_ssl.h"
45
#include "fence.h"
46
#include "loader_talk.h"
47
#include "logging.h"
48
#include "options.h"
49
#include "platform.h"
50
#include "sanitizer.h"
51
#include "util/posix.h"
52
#include "util/string.h"
53
54
using namespace std;  // NOLINT
55
56
namespace loader {
57
58
// Follow the fuse convention for option parsing
59
struct CvmfsOptions {
60
  char *config;
61
  int uid;
62
  int gid;
63
  int grab_mountpoint;
64
  int cvmfs_suid;
65
  int disable_watchdog;
66
  int simple_options_parsing;
67
68
  // Ignored options
69
  int ign_netdev;
70
  int ign_user;
71
  int ign_nouser;
72
  int ign_users;
73
  int ign_auto;
74
  int ign_noauto;
75
};
76
77
enum {
78
  KEY_HELP,
79
  KEY_VERSION,
80
  KEY_FOREGROUND,
81
  KEY_SINGLETHREAD,
82
  KEY_FUSE_DEBUG,
83
  KEY_CVMFS_DEBUG,
84
  KEY_OPTIONS_PARSE,
85
};
86
#define CVMFS_OPT(t, p, v) { t, offsetof(struct CvmfsOptions, p), v }
87
#define CVMFS_SWITCH(t, p) { t, offsetof(struct CvmfsOptions, p), 1 }
88
static struct fuse_opt cvmfs_array_opts[] = {
89
  CVMFS_OPT("config=%s",                    config, 0),
90
  CVMFS_OPT("uid=%d",                       uid, 0),
91
  CVMFS_OPT("gid=%d",                       gid, 0),
92
  CVMFS_SWITCH("grab_mountpoint",           grab_mountpoint),
93
  CVMFS_SWITCH("cvmfs_suid",                cvmfs_suid),
94
  CVMFS_SWITCH("disable_watchdog",          disable_watchdog),
95
  CVMFS_SWITCH("simple_options_parsing",    simple_options_parsing),
96
97
  // Ignore these options
98
  CVMFS_SWITCH("_netdev",          ign_netdev),
99
  CVMFS_SWITCH("user",             ign_user),
100
  CVMFS_SWITCH("nouser",           ign_nouser),
101
  CVMFS_SWITCH("users",            ign_users),
102
  CVMFS_SWITCH("auto",             ign_auto),
103
  CVMFS_SWITCH("noauto",           ign_noauto),
104
105
  FUSE_OPT_KEY("-V",            KEY_VERSION),
106
  FUSE_OPT_KEY("--version",     KEY_VERSION),
107
  FUSE_OPT_KEY("-h",            KEY_HELP),
108
  FUSE_OPT_KEY("--help",        KEY_HELP),
109
  FUSE_OPT_KEY("-f",            KEY_FOREGROUND),
110
  FUSE_OPT_KEY("-d",            KEY_FUSE_DEBUG),
111
  FUSE_OPT_KEY("debug",         KEY_CVMFS_DEBUG),
112
  FUSE_OPT_KEY("-s",            KEY_SINGLETHREAD),
113
  FUSE_OPT_KEY("parse",         KEY_OPTIONS_PARSE),
114
  FUSE_OPT_KEY("-k",            KEY_OPTIONS_PARSE),
115
  {0, 0, 0},
116
};
117
118
119
string *repository_name_ = NULL;
120
string *mount_point_ = NULL;
121
string *config_files_ = NULL;
122
string *socket_path_ = NULL;
123
string *usyslog_path_ = NULL;
124
uid_t uid_ = 0;
125
gid_t gid_ = 0;
126
bool single_threaded_ = false;
127
bool foreground_ = false;
128
bool debug_mode_ = false;
129
bool grab_mountpoint_ = false;
130
bool parse_options_only_ = false;
131
bool suid_mode_ = false;
132
bool disable_watchdog_ = false;
133
bool simple_options_parsing_ = false;
134
void *library_handle_;
135
Fence *fence_reload_;
136
CvmfsExports *cvmfs_exports_;
137
LoaderExports *loader_exports_;
138
139
140
static void Usage(const string &exename) {
141
  LogCvmfs(kLogCvmfs, kLogStdout,
142
    "The CernVM File System\n"
143
    "Version %s\n"
144
    "Copyright (c) 2009- CERN, all rights reserved\n\n"
145
    "Please visit http://cernvm.cern.ch for details.\n\n"
146
    "Usage: %s [-h] [-V] [-s] [-f] [-d] [-k] [-o mount options] "
147
      "<repository name> <mount point>\n\n"
148
    "CernVM-FS general options:\n"
149
    "  --help|-h            Print Help output (this)\n"
150
    "  --version|-V         Print CernVM-FS version\n"
151
    "  -s                   Run singlethreaded\n"
152
    "  -f                   Run in foreground\n"
153
    "  -d                   Enable debugging\n"
154
    "  -k                   Parse options\n"
155
    "CernVM-FS mount options:\n"
156
    "  -o config=FILES      colon-separated path list of config files\n"
157
    "  -o uid=UID           Drop credentials to another user\n"
158
    "  -o gid=GID           Drop credentials to another group\n"
159
    "  -o grab_mountpoint   give ownership of the mountpoint to the user "
160
                            "before mounting (required for autofs)\n"
161
    "  -o parse             Parse and print cvmfs parameters\n"
162
    "  -o cvmfs_suid        Enable suid mode\n\n"
163
    "  -o disable_watchdog  Do not spawn a post mortem crash handler\n"
164
    "Fuse mount options:\n"
165
    "  -o allow_other       allow access to other users\n"
166
    "  -o allow_root        allow access to root\n"
167
    "  -o nonempty          allow mounts over non-empty directory\n",
168
    PACKAGE_VERSION, exename.c_str());
169
}
170
171
172
static void stub_init(void *userdata, struct fuse_conn_info *conn) {
173
  FenceGuard fence_guard(fence_reload_);
174
  cvmfs_exports_->cvmfs_operations.init(userdata, conn);
175
}
176
177
178
static void stub_destroy(void *userdata) {
179
  FenceGuard fence_guard(fence_reload_);
180
  cvmfs_exports_->cvmfs_operations.destroy(userdata);
181
}
182
183
184
static void stub_lookup(fuse_req_t req, fuse_ino_t parent,
185
                        const char *name)
186
{
187
  FenceGuard fence_guard(fence_reload_);
188
  cvmfs_exports_->cvmfs_operations.lookup(req, parent, name);
189
}
190
191
192
static void stub_getattr(fuse_req_t req, fuse_ino_t ino,
193
                         struct fuse_file_info *fi)
194
{
195
  FenceGuard fence_guard(fence_reload_);
196
  cvmfs_exports_->cvmfs_operations.getattr(req, ino, fi);
197
}
198
199
200
static void stub_readlink(fuse_req_t req, fuse_ino_t ino) {
201
  FenceGuard fence_guard(fence_reload_);
202
  cvmfs_exports_->cvmfs_operations.readlink(req, ino);
203
}
204
205
206
static void stub_opendir(fuse_req_t req, fuse_ino_t ino,
207
                         struct fuse_file_info *fi)
208
{
209
  FenceGuard fence_guard(fence_reload_);
210
  cvmfs_exports_->cvmfs_operations.opendir(req, ino, fi);
211
}
212
213
214
static void stub_releasedir(fuse_req_t req, fuse_ino_t ino,
215
                            struct fuse_file_info *fi)
216
{
217
  FenceGuard fence_guard(fence_reload_);
218
  cvmfs_exports_->cvmfs_operations.releasedir(req, ino, fi);
219
}
220
221
222
static void stub_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
223
                         off_t off, struct fuse_file_info *fi)
224
{
225
  FenceGuard fence_guard(fence_reload_);
226
  cvmfs_exports_->cvmfs_operations.readdir(req, ino, size, off, fi);
227
}
228
229
230
static void stub_open(fuse_req_t req, fuse_ino_t ino,
231
                      struct fuse_file_info *fi)
232
{
233
  FenceGuard fence_guard(fence_reload_);
234
  cvmfs_exports_->cvmfs_operations.open(req, ino, fi);
235
}
236
237
238
static void stub_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
239
                       struct fuse_file_info *fi)
240
{
241
  FenceGuard fence_guard(fence_reload_);
242
  cvmfs_exports_->cvmfs_operations.read(req, ino, size, off, fi);
243
}
244
245
246
static void stub_release(fuse_req_t req, fuse_ino_t ino,
247
                         struct fuse_file_info *fi)
248
{
249
  FenceGuard fence_guard(fence_reload_);
250
  cvmfs_exports_->cvmfs_operations.release(req, ino, fi);
251
}
252
253
254
static void stub_statfs(fuse_req_t req, fuse_ino_t ino) {
255
  FenceGuard fence_guard(fence_reload_);
256
  cvmfs_exports_->cvmfs_operations.statfs(req, ino);
257
}
258
259
260
#ifdef __APPLE__
261
static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
262
                          size_t size, uint32_t position)
263
#else
264
static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
265
                          size_t size)
266
#endif
267
{
268
  FenceGuard fence_guard(fence_reload_);
269
#ifdef __APPLE__
270
  cvmfs_exports_->cvmfs_operations.getxattr(req, ino, name, size, position);
271
#else
272
  cvmfs_exports_->cvmfs_operations.getxattr(req, ino, name, size);
273
#endif
274
}
275
276
277
static void stub_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
278
  FenceGuard fence_guard(fence_reload_);
279
  cvmfs_exports_->cvmfs_operations.listxattr(req, ino, size);
280
}
281
282
283
static void stub_forget(
284
  fuse_req_t req,
285
  fuse_ino_t ino,
286
  unsigned long nlookup  // NOLINT
287
) {
288
  FenceGuard fence_guard(fence_reload_);
289
  cvmfs_exports_->cvmfs_operations.forget(req, ino, nlookup);
290
}
291
292
293
/**
294
 * The callback used when fuse is parsing all the options
295
 * We separate CVMFS options from FUSE options here.
296
 *
297
 * \return On success zero, else non-zero
298
 */
299
static int ParseFuseOptions(void *data __attribute__((unused)), const char *arg,
300
                            int key, struct fuse_args *outargs)
301
{
302
  unsigned arglen = 0;
303
  if (arg)
304
    arglen = strlen(arg);
305
  switch (key) {
306
    case FUSE_OPT_KEY_OPT:
307
      // Check if it a cvmfs option
308
      if ((arglen > 0) && (arg[0] != '-')) {
309
        const char **o;
310
        for (o = (const char**)cvmfs_array_opts; *o; o++) {
311
          unsigned olen = strlen(*o);
312
          if ((arglen > olen && arg[olen] == '=') &&
313
              (strncasecmp(arg, *o, olen) == 0))
314
            return 0;
315
        }
316
      }
317
      return 1;
318
319
    case FUSE_OPT_KEY_NONOPT:
320
      // first: repository name, second: mount point
321
      assert(arg != NULL);
322
      if (!repository_name_) {
323
        repository_name_ = new string(arg);
324
      } else {
325
        if (mount_point_)
326
          return 1;
327
        mount_point_ = new string(arg);
328
      }
329
      return 0;
330
331
    case KEY_HELP:
332
      Usage(outargs->argv[0]);
333
      exit(0);
334
    case KEY_VERSION:
335
      LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS version %s\n",
336
               PACKAGE_VERSION);
337
      exit(0);
338
    case KEY_FOREGROUND:
339
      foreground_ = true;
340
      return 0;
341
    case KEY_SINGLETHREAD:
342
      single_threaded_ = true;
343
      return 0;
344
    case KEY_FUSE_DEBUG:
345
      fuse_opt_add_arg(outargs, "-d");
346
    case KEY_CVMFS_DEBUG:
347
      debug_mode_ = true;
348
      return 0;
349
    case KEY_OPTIONS_PARSE:
350
      parse_options_only_ = true;
351
      return 0;
352
    default:
353
      LogCvmfs(kLogCvmfs, kLogStderr, "internal option parsing error");
354
      abort();
355
  }
356
}
357
358
359
static fuse_args *ParseCmdLine(int argc, char *argv[]) {
360
  struct fuse_args *mount_options = new fuse_args();
361
  CvmfsOptions cvmfs_options;
362
  memset(&cvmfs_options, 0, sizeof(cvmfs_options));
363
364
  mount_options->argc = argc;
365
  mount_options->argv = argv;
366
  mount_options->allocated = 0;
367
  if ((fuse_opt_parse(mount_options, &cvmfs_options, cvmfs_array_opts,
368
                      ParseFuseOptions) != 0) ||
369
      !mount_point_ || !repository_name_)
370
  {
371
    delete mount_options;
372
    return NULL;
373
  }
374
  if (cvmfs_options.config) {
375
    config_files_ = new string(cvmfs_options.config);
376
    free(cvmfs_options.config);
377
  }
378
  uid_ = cvmfs_options.uid;
379
  gid_ = cvmfs_options.gid;
380
  grab_mountpoint_ = cvmfs_options.grab_mountpoint;
381
  suid_mode_ = cvmfs_options.cvmfs_suid;
382
  disable_watchdog_ = cvmfs_options.disable_watchdog;
383
  simple_options_parsing_ = cvmfs_options.simple_options_parsing;
384
385
  return mount_options;
386
}
387
388
389
static void SetFuseOperations(struct fuse_lowlevel_ops *loader_operations) {
390
  memset(loader_operations, 0, sizeof(*loader_operations));
391
392
  loader_operations->init        = stub_init;
393
  loader_operations->destroy     = stub_destroy;
394
395
  loader_operations->lookup      = stub_lookup;
396
  loader_operations->getattr     = stub_getattr;
397
  loader_operations->readlink    = stub_readlink;
398
  loader_operations->open        = stub_open;
399
  loader_operations->read        = stub_read;
400
  loader_operations->release     = stub_release;
401
  loader_operations->opendir     = stub_opendir;
402
  loader_operations->readdir     = stub_readdir;
403
  loader_operations->releasedir  = stub_releasedir;
404
  loader_operations->statfs      = stub_statfs;
405
  loader_operations->getxattr    = stub_getxattr;
406
  loader_operations->listxattr   = stub_listxattr;
407
  loader_operations->forget      = stub_forget;
408
}
409
410
411
static void *OpenLibrary(const string &path) {
412
  return dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
413
}
414
415
416
static void CloseLibrary() {
417
#ifdef HAS_VALGRIND_HEADERS
418
  // If the libcvmfs_fuse library is unloaded, valgrind can't resolve the
419
  // symbols anymore.  We skip under valgrind.
420
  if (!RUNNING_ON_VALGRIND) {
421
#endif
422
    dlclose(library_handle_);
423
    library_handle_ = NULL;
424
#ifdef HAS_VALGRIND_HEADERS
425
  }
426
#endif
427
}
428
429
430
static CvmfsExports *LoadLibrary(const bool debug_mode,
431
                                 LoaderExports *loader_exports)
432
{
433
  string library_name = string("cvmfs_fuse") + ((debug_mode) ? "_debug" : "");
434
  library_name = platform_libname(library_name);
435
  string error_messages;
436
437
  static vector<string> library_paths;  // TODO(rmeusel): C++11 initializer
438
  if (library_paths.empty()) {
439
    library_paths.push_back(library_name);
440
    library_paths.push_back("/usr/lib/"   + library_name);
441
    library_paths.push_back("/usr/lib64/" + library_name);
442
#ifdef __APPLE__
443
    // Since OS X El Capitan (10.11) came with SIP, we needed to relocate our
444
    // binaries from /usr/... to /usr/local/...
445
    library_paths.push_back("/usr/local/lib/" + library_name);
446
#endif
447
  }
448
449
  vector<string>::const_iterator i    = library_paths.begin();
450
  vector<string>::const_iterator iend = library_paths.end();
451
  for (; i != iend; ++i) {  // TODO(rmeusel): C++11 range based for
452
    library_handle_ = OpenLibrary(*i);
453
    if (library_handle_ != NULL) {
454
      break;
455
    }
456
457
    error_messages += string(dlerror()) + "\n";
458
  }
459
460
  if (!library_handle_) {
461
    LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
462
             "failed to load cvmfs library, tried: '%s'\n%s",
463
             JoinStrings(library_paths, "' '").c_str(), error_messages.c_str());
464
    return NULL;
465
  }
466
467
  CvmfsExports **exports_ptr = reinterpret_cast<CvmfsExports **>(
468
    dlsym(library_handle_, "g_cvmfs_exports"));
469
  if (!exports_ptr)
470
    return NULL;
471
472
  if (loader_exports) {
473
    LoadEvent *load_event = new LoadEvent();
474
    load_event->timestamp = time(NULL);
475
    load_event->so_version = (*exports_ptr)->so_version;
476
    loader_exports->history.push_back(load_event);
477
  }
478
479
  return *exports_ptr;
480
}
481
482
483
Failures Reload(const int fd_progress, const bool stop_and_go) {
484
  int retval;
485
486
  retval = cvmfs_exports_->fnMaintenanceMode(fd_progress);
487
  if (!retval)
488
    return kFailMaintenanceMode;
489
490
  SendMsg2Socket(fd_progress, "Blocking new file system calls\n");
491
  fence_reload_->Close();
492
493
  SendMsg2Socket(fd_progress, "Waiting for active file system calls\n");
494
  fence_reload_->Drain();
495
496
  retval = cvmfs_exports_->fnSaveState(fd_progress,
497
                                       &loader_exports_->saved_states);
498
  if (!retval)
499
    return kFailSaveState;
500
501
  SendMsg2Socket(fd_progress, "Unloading Fuse module\n");
502
  cvmfs_exports_->fnFini();
503
  CloseLibrary();
504
505
  if (stop_and_go) {
506
    CreateFile(*socket_path_ + ".paused", 0600);
507
    SendMsg2Socket(fd_progress, "Waiting for the delivery of SIGUSR1...\n");
508
    WaitForSignal(SIGUSR1);
509
    unlink((*socket_path_ + ".paused").c_str());
510
  }
511
512
  SendMsg2Socket(fd_progress, "Re-Loading Fuse module\n");
513
  cvmfs_exports_ = LoadLibrary(debug_mode_, loader_exports_);
514
  if (!cvmfs_exports_)
515
    return kFailLoadLibrary;
516
  retval = cvmfs_exports_->fnInit(loader_exports_);
517
  if (retval != kFailOk) {
518
    string msg_progress = cvmfs_exports_->fnGetErrorMsg() + " (" +
519
                          StringifyInt(retval) + ")\n";
520
    LogCvmfs(kLogCvmfs, kLogSyslogErr, "%s", msg_progress.c_str());
521
    SendMsg2Socket(fd_progress, msg_progress);
522
    return (Failures)retval;
523
  }
524
525
  retval = cvmfs_exports_->fnRestoreState(fd_progress,
526
                                          loader_exports_->saved_states);
527
  if (!retval)
528
    return kFailRestoreState;
529
  cvmfs_exports_->fnFreeSavedState(fd_progress, loader_exports_->saved_states);
530
  for (unsigned i = 0, l = loader_exports_->saved_states.size(); i < l; ++i) {
531
    delete loader_exports_->saved_states[i];
532
  }
533
  loader_exports_->saved_states.clear();
534
535
  SendMsg2Socket(fd_progress, "Activating Fuse module\n");
536
  cvmfs_exports_->fnSpawn();
537
538
  fence_reload_->Open();
539
  return kFailOk;
540
}
541
542
}  // namespace loader
543
544
545
using namespace loader;  // NOLINT(build/namespaces)
546
547
// Making OpenSSL (libcrypto) thread-safe
548
#ifndef OPENSSL_API_INTERFACE_V11
549
550
pthread_mutex_t *gLibcryptoLocks;
551
552
static void CallbackLibcryptoLock(int mode, int type,
553
                                  const char *file, int line) {
554
  (void)file;
555
  (void)line;
556
557
  int retval;
558
559
  if (mode & CRYPTO_LOCK) {
560
    retval = pthread_mutex_lock(&(gLibcryptoLocks[type]));
561
  } else {
562
    retval = pthread_mutex_unlock(&(gLibcryptoLocks[type]));
563
  }
564
  assert(retval == 0);
565
}
566
567
static unsigned long CallbackLibcryptoThreadId() {  // NOLINT(runtime/int)
568
  return platform_gettid();
569
}
570
571
#endif
572
573
static void SetupLibcryptoMt() {
574
#ifndef OPENSSL_API_INTERFACE_V11
575
  gLibcryptoLocks = static_cast<pthread_mutex_t *>(OPENSSL_malloc(
576
    CRYPTO_num_locks() * sizeof(pthread_mutex_t)));
577
  for (int i = 0; i < CRYPTO_num_locks(); ++i) {
578
    int retval = pthread_mutex_init(&(gLibcryptoLocks[i]), NULL);
579
    assert(retval == 0);
580
  }
581
582
  CRYPTO_set_id_callback(CallbackLibcryptoThreadId);
583
  CRYPTO_set_locking_callback(CallbackLibcryptoLock);
584
#endif
585
}
586
587
static void CleanupLibcryptoMt(void) {
588
#ifndef OPENSSL_API_INTERFACE_V11
589
  CRYPTO_set_locking_callback(NULL);
590
  for (int i = 0; i < CRYPTO_num_locks(); ++i)
591
    pthread_mutex_destroy(&(gLibcryptoLocks[i]));
592
593
  OPENSSL_free(gLibcryptoLocks);
594
#endif
595
}
596
597
598
int main(int argc, char *argv[]) {
599
  // Set a decent umask for new files (no write access to group/everyone).
600
  // We want to allow group write access for the talk-socket.
601
  umask(007);
602
  // SIGUSR1 is used for the stop_and_go mode during reload
603
  BlockSignal(SIGUSR1);
604
605
  int retval;
606
607
  // Jump into alternative process flavors (e.g. shared cache manager)
608
  // We are here due to a fork+execve (ManagedExec in util.cc) or due to
609
  // utility calls of cvmfs2
610
  if ((argc > 1) && (strstr(argv[1], "__") == argv[1])) {
611
    if (string(argv[1]) == string("__RELOAD__")) {
612
      if (argc < 3)
613
        return 1;
614
      bool stop_and_go = false;
615
      if ((argc > 3) && (string(argv[3]) == "stop_and_go"))
616
        stop_and_go = true;
617
      retval = loader_talk::MainReload(argv[2], stop_and_go);
618
      if ((retval != 0) && (stop_and_go)) {
619
        CreateFile(string(argv[2]) + ".paused.crashed", 0600);
620
      }
621
      return retval;
622
    }
623
624
    if (string(argv[1]) == string("__MK_ALIEN_CACHE__")) {
625
      if (argc < 5)
626
        return 1;
627
      string alien_cache_dir = argv[2];
628
      sanitizer::PositiveIntegerSanitizer sanitizer;
629
      if (!sanitizer.IsValid(argv[3]) || !sanitizer.IsValid(argv[4]))
630
        return 1;
631
      uid_t uid_owner = String2Uint64(argv[3]);
632
      gid_t gid_owner = String2Uint64(argv[4]);
633
634
      int retval = MkdirDeep(alien_cache_dir, 0770);
635
      if (!retval) {
636
        LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create %s",
637
                 alien_cache_dir.c_str());
638
        return 1;
639
      }
640
      retval = chown(alien_cache_dir.c_str(), uid_owner, gid_owner);
641
      if (retval != 0) {
642
        LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set owner of %s to %d:%d",
643
                 alien_cache_dir.c_str(), uid_owner, gid_owner);
644
        return 1;
645
      }
646
      retval = SwitchCredentials(uid_owner, gid_owner, false);
647
      if (!retval) {
648
        LogCvmfs(kLogCvmfs, kLogStderr, "Failed to impersonate %d:%d",
649
                 uid_owner, gid_owner);
650
        return 1;
651
      }
652
      // Allow access to user and group
653
      retval = MakeCacheDirectories(alien_cache_dir, 0770);
654
      if (!retval) {
655
        LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create cache skeleton");
656
        return 1;
657
      }
658
      return 0;
659
    }
660
661
    debug_mode_ = getenv("__CVMFS_DEBUG_MODE__") != NULL;
662
    cvmfs_exports_ = LoadLibrary(debug_mode_, NULL);
663
    if (!cvmfs_exports_)
664
      return kFailLoadLibrary;
665
    return cvmfs_exports_->fnAltProcessFlavor(argc, argv);
666
  }
667
668
  SetupLibcryptoMt();
669
670
  // Option parsing
671
  struct fuse_args *mount_options;
672
  mount_options = ParseCmdLine(argc, argv);
673
  if (!mount_options) {
674
    Usage(argv[0]);
675
    return kFailOptions;
676
  }
677
678
  string parameter;
679
  OptionsManager *options_manager;
680
  if (simple_options_parsing_) {
681
    options_manager = new SimpleOptionsParser(
682
      new DefaultOptionsTemplateManager(*repository_name_));
683
  } else {
684
    options_manager = new BashOptionsManager(
685
      new DefaultOptionsTemplateManager(*repository_name_));
686
  }
687
  if (config_files_) {
688
    vector<string> tokens = SplitString(*config_files_, ':');
689
    for (unsigned i = 0, s = tokens.size(); i < s; ++i) {
690
      options_manager->ParsePath(tokens[i], false);
691
    }
692
  } else {
693
    options_manager->ParseDefault(*repository_name_);
694
  }
695
696
#ifdef __APPLE__
697
  string volname = "-ovolname=" + *repository_name_;
698
  fuse_opt_add_arg(mount_options, volname.c_str());
699
  // Allow for up to 5 minute "hangs" before OS X may kill cvmfs
700
  fuse_opt_add_arg(mount_options, "-odaemon_timeout=300");
701
  fuse_opt_add_arg(mount_options, "-onoapplexattr");
702
  // Should libfuse be single-threaded?  See CVM-871, CVM-855
703
  // single_threaded_ = true;
704
#endif
705
  if (options_manager->GetValue("CVMFS_MOUNT_RW", &parameter) &&
706
      options_manager->IsOn(parameter))
707
  {
708
    fuse_opt_add_arg(mount_options, "-orw");
709
  } else {
710
    fuse_opt_add_arg(mount_options, "-oro");
711
  }
712
  fuse_opt_add_arg(mount_options, "-onodev");
713
  if (options_manager->GetValue("CVMFS_SUID", &parameter) &&
714
      options_manager->IsOn(parameter))
715
  {
716
    suid_mode_ = true;
717
  }
718
  if (suid_mode_) {
719
    if (getuid() != 0) {
720
      LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
721
               "must be root to mount with suid option");
722
      abort();
723
    }
724
    fuse_opt_add_arg(mount_options, "-osuid");
725
    LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with suid support");
726
  }
727
  loader_exports_ = new LoaderExports();
728
  loader_exports_->loader_version = PACKAGE_VERSION;
729
  loader_exports_->boot_time = time(NULL);
730
  loader_exports_->program_name = argv[0];
731
  loader_exports_->foreground = foreground_;
732
  loader_exports_->repository_name = *repository_name_;
733
  loader_exports_->mount_point = *mount_point_;
734
  loader_exports_->disable_watchdog = disable_watchdog_;
735
  loader_exports_->simple_options_parsing = simple_options_parsing_;
736
  if (config_files_)
737
    loader_exports_->config_files = *config_files_;
738
  else
739
    loader_exports_->config_files = "";
740
741
  if (parse_options_only_) {
742
    LogCvmfs(kLogCvmfs, kLogStdout, "# CernVM-FS parameters:\n%s",
743
             options_manager->Dump().c_str());
744
    return 0;
745
  }
746
747
  // Logging
748
  if (options_manager->GetValue("CVMFS_SYSLOG_LEVEL", &parameter))
749
    SetLogSyslogLevel(String2Uint64(parameter));
750
  else
751
    SetLogSyslogLevel(3);
752
  if (options_manager->GetValue("CVMFS_SYSLOG_FACILITY", &parameter))
753
    SetLogSyslogFacility(String2Int64(parameter));
754
  SetLogSyslogPrefix(*repository_name_);
755
  // Deferr setting usyslog until credentials are dropped
756
757
  // Permissions check
758
  if (options_manager->GetValue("CVMFS_CHECK_PERMISSIONS", &parameter)) {
759
    if (options_manager->IsOn(parameter)) {
760
      fuse_opt_add_arg(mount_options, "-odefault_permissions");
761
    }
762
  }
763
764
  if (!DirectoryExists(*mount_point_)) {
765
    LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
766
             "Moint point %s does not exist", mount_point_->c_str());
767
    return kFailPermission;
768
  }
769
770
  // Number of file descriptors
771
  if (options_manager->GetValue("CVMFS_NFILES", &parameter)) {
772
    int retval = SetLimitNoFile(String2Uint64(parameter));
773
    if (retval == -2) {
774
      LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running under valgrind");
775
    } else if (retval == -1) {
776
      LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
777
               "Failed to set maximum number of open files, "
778
               "insufficient permissions");
779
      return kFailPermission;
780
    }
781
  }
782
783
  // Apply OOM score adjustment
784
  if (options_manager->GetValue("CVMFS_OOM_SCORE_ADJ", &parameter)) {
785
    string proc_path = "/proc/" + StringifyInt(getpid()) + "/oom_score_adj";
786
    int fd_oom = open(proc_path.c_str(), O_WRONLY);
787
    if (fd_oom < 0) {
788
      LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn,
789
               "failed to open %s", proc_path.c_str());
790
    } else {
791
      bool retval = SafeWrite(fd_oom, parameter.data(), parameter.length());
792
      if (!retval) {
793
        LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn,
794
                 "failed to set OOM score adjustment to %s", parameter.c_str());
795
      }
796
      close(fd_oom);
797
    }
798
  }
799
800
  // Protect the process from being killed by systemd
801
  if (options_manager->GetValue("CVMFS_SYSTEMD_NOKILL", &parameter) &&
802
      options_manager->IsOn(parameter))
803
  {
804
    argv[0][0] = '@';
805
  }
806
807
  // Grab mountpoint
808
  if (grab_mountpoint_) {
809
    if ((chown(mount_point_->c_str(), uid_, gid_) != 0) ||
810
        (chmod(mount_point_->c_str(), 0755) != 0))
811
    {
812
      LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
813
               "Failed to grab mountpoint (%d)", errno);
814
      return kFailPermission;
815
    }
816
  }
817
818
  // Drop credentials
819
  if ((uid_ != 0) || (gid_ != 0)) {
820
    LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with credentials %d:%d",
821
             uid_, gid_);
822
    const bool retrievable = (suid_mode_ || !disable_watchdog_);
823
    if (!SwitchCredentials(uid_, gid_, retrievable)) {
824
      LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
825
               "Failed to drop credentials");
826
      return kFailPermission;
827
    }
828
  }
829
830
  // Only set usyslog now, otherwise file permissions are wrong
831
  usyslog_path_ = new string();
832
  if (options_manager->GetValue("CVMFS_USYSLOG", &parameter))
833
    *usyslog_path_ = parameter;
834
  SetLogMicroSyslog(*usyslog_path_);
835
836
  if (single_threaded_) {
837
    LogCvmfs(kLogCvmfs, kLogStdout,
838
             "CernVM-FS: running in single threaded mode");
839
  }
840
  if (debug_mode_) {
841
    LogCvmfs(kLogCvmfs, kLogStdout | kLogSyslogWarn,
842
             "CernVM-FS: running in debug mode");
843
  }
844
845
  // Initialize the loader socket, connections are not accepted until Spawn()
846
  socket_path_ = new string("/var/run/cvmfs");
847
  if (options_manager->GetValue("CVMFS_RELOAD_SOCKETS", &parameter))
848
    *socket_path_ = MakeCanonicalPath(parameter);
849
  *socket_path_ += "/cvmfs." + *repository_name_;
850
  retval = loader_talk::Init(*socket_path_);
851
  if (!retval) {
852
    LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
853
             "Failed to initialize loader socket");
854
    return kFailLoaderTalk;
855
  }
856
857
  // Options are not needed anymore
858
  delete options_manager;
859
  options_manager = NULL;
860
861
  struct fuse_chan *channel;
862
  loader_exports_->fuse_channel = &channel;
863
864
  // Load and initialize cvmfs library
865
  LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak,
866
           "CernVM-FS: loading Fuse module... ");
867
  cvmfs_exports_ = LoadLibrary(debug_mode_, loader_exports_);
868
  if (!cvmfs_exports_) {
869
    return kFailLoadLibrary;
870
  }
871
  retval = cvmfs_exports_->fnInit(loader_exports_);
872
  if (retval != kFailOk) {
873
    if (retval == kFailDoubleMount) {
874
      LogCvmfs(kLogCvmfs, kLogStderr,
875
               "\nCernVM-FS: repository %s already mounted on %s",
876
               loader_exports_->repository_name.c_str(),
877
               loader_exports_->mount_point.c_str());
878
      return 0;
879
    }
880
    LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "%s (%d - %s)",
881
             cvmfs_exports_->fnGetErrorMsg().c_str(),
882
             retval, Code2Ascii((Failures)retval));
883
    cvmfs_exports_->fnFini();
884
    return retval;
885
  }
886
  LogCvmfs(kLogCvmfs, kLogStdout, "done");
887
888
  // Mount
889
  fence_reload_ = new Fence();
890
891
  if (suid_mode_) {
892
    const bool retrievable = true;
893
    if (!SwitchCredentials(0, getgid(), retrievable)) {
894
      LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
895
               "failed to re-gain root permissions for mounting");
896
      cvmfs_exports_->fnFini();
897
      return kFailPermission;
898
    }
899
  }
900
901
  channel = fuse_mount(mount_point_->c_str(), mount_options);
902
  if (!channel) {
903
    LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
904
             "failed to create Fuse channel");
905
    cvmfs_exports_->fnFini();
906
    return kFailMount;
907
  }
908
909
  // drop credentials
910
  if (suid_mode_) {
911
    const bool retrievable = !disable_watchdog_;
912
    if (!SwitchCredentials(uid_, gid_, retrievable)) {
913
      LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
914
               "failed to drop permissions after mounting");
915
      cvmfs_exports_->fnFini();
916
      return kFailPermission;
917
    }
918
  }
919
920
  struct fuse_lowlevel_ops loader_operations;
921
  SetFuseOperations(&loader_operations);
922
  struct fuse_session *session;
923
  session = fuse_lowlevel_new(mount_options, &loader_operations,
924
                              sizeof(loader_operations), NULL);
925
  if (!session) {
926
    LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
927
             "failed to create Fuse session");
928
    fuse_unmount(mount_point_->c_str(), channel);
929
    cvmfs_exports_->fnFini();
930
    return kFailMount;
931
  }
932
933
  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: mounted cvmfs on %s",
934
           mount_point_->c_str());
935
  LogCvmfs(kLogCvmfs, kLogSyslog,
936
           "CernVM-FS: linking %s to repository %s",
937
           mount_point_->c_str(), repository_name_->c_str());
938
  if (!foreground_)
939
    Daemonize();
940
941
  cvmfs_exports_->fnSpawn();
942
  loader_talk::Spawn();
943
944
  SetLogMicroSyslog("");
945
  retval = fuse_set_signal_handlers(session);
946
  assert(retval == 0);
947
  fuse_session_add_chan(session, channel);
948
  if (single_threaded_)
949
    retval = fuse_session_loop(session);
950
  else
951
    retval = fuse_session_loop_mt(session);
952
  SetLogMicroSyslog(*usyslog_path_);
953
954
  loader_talk::Fini();
955
  cvmfs_exports_->fnFini();
956
957
  // Unmount
958
  fuse_session_remove_chan(channel);
959
  fuse_remove_signal_handlers(session);
960
  fuse_session_destroy(session);
961
  fuse_unmount(mount_point_->c_str(), channel);
962
  fuse_opt_free_args(mount_options);
963
  delete mount_options;
964
  channel = NULL;
965
  session = NULL;
966
  mount_options = NULL;
967
968
  CloseLibrary();
969
970
  LogCvmfs(kLogCvmfs, kLogSyslog, "CernVM-FS: unmounted %s (%s)",
971
           mount_point_->c_str(), repository_name_->c_str());
972
973
  CleanupLibcryptoMt();
974
975
  delete fence_reload_;
976
  delete loader_exports_;
977
  delete config_files_;
978
  delete repository_name_;
979
  delete mount_point_;
980
  delete socket_path_;
981
  fence_reload_ = NULL;
982
  loader_exports_ = NULL;
983
  config_files_ = NULL;
984
  repository_name_ = NULL;
985
  mount_point_ = NULL;
986
  socket_path_ = NULL;
987
988
  if (retval != 0)
989
    return kFailFuseLoop;
990
  return kFailOk;
991
}