CernVM-FS  2.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
loader.cc
Go to the documentation of this file.
1 
12 #define ENOATTR ENODATA
13 #define _FILE_OFFSET_BITS 64
14 
15 #include "cvmfs_config.h"
16 #include "loader.h"
17 
18 #include <dlfcn.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <openssl/crypto.h>
22 #include <sched.h>
23 #include <signal.h>
24 #include <stddef.h>
25 #include <sys/resource.h>
26 #include <time.h>
27 #include <unistd.h>
28 // If valgrind headers are present on the build system, then we can detect
29 // valgrind at runtime.
30 #ifdef HAS_VALGRIND_HEADERS
31 #include <valgrind/valgrind.h>
32 #endif
33 
34 #include <cassert>
35 #include <cstdlib>
36 #include <cstring>
37 #include <string>
38 #include <vector>
39 
40 #include "atomic.h"
41 #include "duplex_fuse.h"
42 #include "duplex_ssl.h"
43 #include "fence.h"
44 #include "fuse_main.h"
45 #include "loader_talk.h"
46 #include "logging.h"
47 #include "options.h"
48 #include "platform.h"
49 #include "sanitizer.h"
50 #include "util/exception.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;
70 
71  // Ignored options
73  int ign_user;
75  int ign_users;
76  int ign_auto;
79 };
80 
81 enum {
89 };
90 #define CVMFS_OPT(t, p, v) { t, offsetof(struct CvmfsOptions, p), v }
91 #define CVMFS_SWITCH(t, p) { t, offsetof(struct CvmfsOptions, p), 1 }
92 static struct fuse_opt cvmfs_array_opts[] = {
93  CVMFS_OPT("config=%s", config, 0),
94  CVMFS_OPT("uid=%d", uid, 0),
95  CVMFS_OPT("gid=%d", gid, 0),
96  CVMFS_SWITCH("system_mount", system_mount),
97  CVMFS_SWITCH("grab_mountpoint", grab_mountpoint),
98  CVMFS_SWITCH("cvmfs_suid", cvmfs_suid),
99  CVMFS_SWITCH("disable_watchdog", disable_watchdog),
100  CVMFS_SWITCH("simple_options_parsing", simple_options_parsing),
101  CVMFS_SWITCH("foreground", foreground),
102  CVMFS_SWITCH("fuse_debug", fuse_debug),
103 
104  // Ignore these options
105  CVMFS_SWITCH("_netdev", ign_netdev),
106  CVMFS_SWITCH("user", ign_user),
107  CVMFS_SWITCH("nouser", ign_nouser),
108  CVMFS_SWITCH("users", ign_users),
109  CVMFS_SWITCH("auto", ign_auto),
110  CVMFS_SWITCH("noauto", ign_noauto),
111  CVMFS_OPT("libfuse=%d", ign_libfuse, 0),
112 
113  FUSE_OPT_KEY("-V", KEY_VERSION),
114  FUSE_OPT_KEY("--version", KEY_VERSION),
115  FUSE_OPT_KEY("-h", KEY_HELP),
116  FUSE_OPT_KEY("--help", KEY_HELP),
117  FUSE_OPT_KEY("-f", KEY_FOREGROUND),
118  FUSE_OPT_KEY("-d", KEY_FUSE_DEBUG),
119  FUSE_OPT_KEY("debug", KEY_CVMFS_DEBUG),
120  FUSE_OPT_KEY("-s", KEY_SINGLETHREAD),
121  FUSE_OPT_KEY("parse", KEY_OPTIONS_PARSE),
122  FUSE_OPT_KEY("-k", KEY_OPTIONS_PARSE),
123  {0, 0, 0},
124 };
125 
126 
127 string *repository_name_ = NULL;
128 string *mount_point_ = NULL;
129 string *config_files_ = NULL;
130 string *socket_path_ = NULL;
131 string *usyslog_path_ = NULL;
132 uid_t uid_ = 0;
133 gid_t gid_ = 0;
134 bool single_threaded_ = false;
135 bool foreground_ = false;
136 bool debug_mode_ = false;
137 bool system_mount_ = false;
138 bool grab_mountpoint_ = false;
139 bool parse_options_only_ = false;
140 bool suid_mode_ = false;
141 bool premounted_ = false;
142 bool disable_watchdog_ = false;
148 
149 
150 static void Usage(const string &exename) {
152  "The CernVM File System\n"
153  "Version %s\n"
154  "Copyright (c) 2009- CERN, all rights reserved\n\n"
155  "Please visit http://cernvm.cern.ch for details.\n\n"
156  "Usage: %s [-h] [-V] [-s] [-f] [-d] [-k] [-o mount options] "
157  "<repository name> <mount point>\n\n"
158  "CernVM-FS general options:\n"
159  " --help|-h Print Help output (this)\n"
160  " --version|-V Print CernVM-FS version\n"
161  " -s Run singlethreaded\n"
162  " -f Run in foreground\n"
163  " -d Enable debugging\n"
164  " -k Parse options\n"
165  "CernVM-FS mount options:\n"
166  " -o config=FILES colon-separated path list of config files\n"
167  " -o uid=UID Drop credentials to another user\n"
168  " -o gid=GID Drop credentials to another group\n"
169  " -o system_mount Indicate that mount is system-wide\n"
170  " -o grab_mountpoint give ownership of the mountpoint to the user "
171  "before mounting (required for autofs)\n"
172  " -o parse Parse and print cvmfs parameters\n"
173  " -o cvmfs_suid Enable suid mode\n\n"
174  " -o disable_watchdog Do not spawn a post mortem crash handler\n"
175  " -o foreground Run in foreground\n"
176  " -o libfuse=[2,3] Enforce a certain libfuse version\n"
177  "Fuse mount options:\n"
178  " -o allow_other allow access to other users\n"
179  " -o allow_root allow access to root\n"
180  " -o nonempty allow mounts over non-empty directory\n",
181  PACKAGE_VERSION, exename.c_str());
182 }
183 
188 bool CheckPremounted(const std::string &mountpoint) {
189  int len;
190  unsigned fd;
191  bool retval = (sscanf(mountpoint.c_str(), "/dev/fd/%u%n", &fd, &len) == 1) &&
192  (len >= 0) &&
193  (static_cast<unsigned>(len) == mountpoint.length());
194  if (retval) {
196  "CernVM-FS: pre-mounted on file descriptor %d", fd);
197  return true;
198  }
199  return false;
200 }
201 
202 
203 static void stub_init(void *userdata, struct fuse_conn_info *conn) {
204  FenceGuard fence_guard(fence_reload_);
205  cvmfs_exports_->cvmfs_operations.init(userdata, conn);
206 }
207 
208 
209 static void stub_destroy(void *userdata) {
210  FenceGuard fence_guard(fence_reload_);
211  cvmfs_exports_->cvmfs_operations.destroy(userdata);
212 }
213 
214 
215 static void stub_lookup(fuse_req_t req, fuse_ino_t parent,
216  const char *name)
217 {
218  FenceGuard fence_guard(fence_reload_);
219  cvmfs_exports_->cvmfs_operations.lookup(req, parent, name);
220 }
221 
222 
223 static void stub_getattr(fuse_req_t req, fuse_ino_t ino,
224  struct fuse_file_info *fi)
225 {
226  FenceGuard fence_guard(fence_reload_);
227  cvmfs_exports_->cvmfs_operations.getattr(req, ino, fi);
228 }
229 
230 
231 static void stub_readlink(fuse_req_t req, fuse_ino_t ino) {
232  FenceGuard fence_guard(fence_reload_);
233  cvmfs_exports_->cvmfs_operations.readlink(req, ino);
234 }
235 
236 
237 static void stub_opendir(fuse_req_t req, fuse_ino_t ino,
238  struct fuse_file_info *fi)
239 {
240  FenceGuard fence_guard(fence_reload_);
241  cvmfs_exports_->cvmfs_operations.opendir(req, ino, fi);
242 }
243 
244 
245 static void stub_releasedir(fuse_req_t req, fuse_ino_t ino,
246  struct fuse_file_info *fi)
247 {
248  FenceGuard fence_guard(fence_reload_);
249  cvmfs_exports_->cvmfs_operations.releasedir(req, ino, fi);
250 }
251 
252 
253 static void stub_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
254  off_t off, struct fuse_file_info *fi)
255 {
256  FenceGuard fence_guard(fence_reload_);
257  cvmfs_exports_->cvmfs_operations.readdir(req, ino, size, off, fi);
258 }
259 
260 
261 static void stub_open(fuse_req_t req, fuse_ino_t ino,
262  struct fuse_file_info *fi)
263 {
264  FenceGuard fence_guard(fence_reload_);
265  cvmfs_exports_->cvmfs_operations.open(req, ino, fi);
266 }
267 
268 
269 static void stub_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
270  struct fuse_file_info *fi)
271 {
272  FenceGuard fence_guard(fence_reload_);
273  cvmfs_exports_->cvmfs_operations.read(req, ino, size, off, fi);
274 }
275 
276 
277 static void stub_release(fuse_req_t req, fuse_ino_t ino,
278  struct fuse_file_info *fi)
279 {
280  FenceGuard fence_guard(fence_reload_);
281  cvmfs_exports_->cvmfs_operations.release(req, ino, fi);
282 }
283 
284 
285 static void stub_statfs(fuse_req_t req, fuse_ino_t ino) {
286  FenceGuard fence_guard(fence_reload_);
287  cvmfs_exports_->cvmfs_operations.statfs(req, ino);
288 }
289 
290 
291 #ifdef __APPLE__
292 static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
293  size_t size, uint32_t position)
294 #else
295 static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
296  size_t size)
297 #endif
298 {
299  FenceGuard fence_guard(fence_reload_);
300 #ifdef __APPLE__
301  cvmfs_exports_->cvmfs_operations.getxattr(req, ino, name, size, position);
302 #else
303  cvmfs_exports_->cvmfs_operations.getxattr(req, ino, name, size);
304 #endif
305 }
306 
307 
308 static void stub_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
309  FenceGuard fence_guard(fence_reload_);
310  cvmfs_exports_->cvmfs_operations.listxattr(req, ino, size);
311 }
312 
313 
314 static void stub_forget(
315  fuse_req_t req,
316  fuse_ino_t ino,
317 #if CVMFS_USE_LIBFUSE == 2
318  unsigned long nlookup // NOLINT
319 #else
320  uint64_t nlookup
321 #endif
322 ) {
323  FenceGuard fence_guard(fence_reload_);
324  cvmfs_exports_->cvmfs_operations.forget(req, ino, nlookup);
325 }
326 
327 
328 #if (FUSE_VERSION >= 29)
329 static void stub_forget_multi(
330  fuse_req_t req,
331  size_t count,
332  struct fuse_forget_data *forgets
333 ) {
334  FenceGuard fence_guard(fence_reload_);
335  cvmfs_exports_->cvmfs_operations.forget_multi(req, count, forgets);
336 }
337 #endif
338 
339 
346 static int ParseFuseOptions(void *data __attribute__((unused)), const char *arg,
347  int key, struct fuse_args *outargs)
348 {
349  unsigned arglen = 0;
350  if (arg)
351  arglen = strlen(arg);
352  switch (key) {
353  case FUSE_OPT_KEY_OPT:
354  // Check if it a cvmfs option
355  if ((arglen > 0) && (arg[0] != '-')) {
356  const char **o;
357  for (o = (const char**)cvmfs_array_opts; *o; o++) {
358  unsigned olen = strlen(*o);
359  if ((arglen > olen && arg[olen] == '=') &&
360  (strncasecmp(arg, *o, olen) == 0))
361  return 0;
362  }
363  }
364  return 1;
365 
366  case FUSE_OPT_KEY_NONOPT:
367  // first: repository name, second: mount point
368  assert(arg != NULL);
369  if (!repository_name_) {
370  repository_name_ = new string(arg);
371  } else {
372  if (mount_point_)
373  return 1;
374  mount_point_ = new string(arg);
376  }
377  return 0;
378 
379  case KEY_HELP:
380  Usage(outargs->argv[0]);
381  exit(0);
382  case KEY_VERSION:
383  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS version %s\n",
384  PACKAGE_VERSION);
385  exit(0);
386  case KEY_FOREGROUND:
387  foreground_ = true;
388  return 0;
389  case KEY_SINGLETHREAD:
390  single_threaded_ = true;
391  return 0;
392  case KEY_FUSE_DEBUG:
393  fuse_opt_add_arg(outargs, "-d");
394  case KEY_CVMFS_DEBUG:
395  debug_mode_ = true;
396  return 0;
397  case KEY_OPTIONS_PARSE:
398  parse_options_only_ = true;
399  return 0;
400  default:
401  PANIC(kLogStderr, "internal option parsing error");
402  }
403 }
404 
405 
406 static fuse_args *ParseCmdLine(int argc, char *argv[]) {
407  struct fuse_args *mount_options = new fuse_args();
409  memset(&cvmfs_options, 0, sizeof(cvmfs_options));
410 
411  mount_options->argc = argc;
412  mount_options->argv = argv;
413  mount_options->allocated = 0;
414  if ((fuse_opt_parse(mount_options, &cvmfs_options, cvmfs_array_opts,
415  ParseFuseOptions) != 0) ||
417  {
418  delete mount_options;
419  return NULL;
420  }
421  if (cvmfs_options.config) {
422  config_files_ = new string(cvmfs_options.config);
423  free(cvmfs_options.config);
424  }
425  uid_ = cvmfs_options.uid;
426  gid_ = cvmfs_options.gid;
427  system_mount_ = cvmfs_options.system_mount;
428  grab_mountpoint_ = cvmfs_options.grab_mountpoint;
429  suid_mode_ = cvmfs_options.cvmfs_suid;
430  disable_watchdog_ = cvmfs_options.disable_watchdog;
432  if (cvmfs_options.foreground) {
433  foreground_ = true;
434  }
435  if (cvmfs_options.fuse_debug) {
436  fuse_opt_add_arg(mount_options, "-d");
437  }
438 
439  return mount_options;
440 }
441 
442 
443 static void SetFuseOperations(struct fuse_lowlevel_ops *loader_operations) {
444  memset(loader_operations, 0, sizeof(*loader_operations));
445 
446  loader_operations->init = stub_init;
447  loader_operations->destroy = stub_destroy;
448 
449  loader_operations->lookup = stub_lookup;
450  loader_operations->getattr = stub_getattr;
451  loader_operations->readlink = stub_readlink;
452  loader_operations->open = stub_open;
453  loader_operations->read = stub_read;
454  loader_operations->release = stub_release;
455  loader_operations->opendir = stub_opendir;
456  loader_operations->readdir = stub_readdir;
457  loader_operations->releasedir = stub_releasedir;
458  loader_operations->statfs = stub_statfs;
459  loader_operations->getxattr = stub_getxattr;
460  loader_operations->listxattr = stub_listxattr;
461  loader_operations->forget = stub_forget;
462 }
463 
464 
465 static void *OpenLibrary(const string &path) {
466  return dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
467 }
468 
469 
470 static void CloseLibrary() {
471 #ifdef HAS_VALGRIND_HEADERS
472  // If the libcvmfs_fuse library is unloaded, valgrind can't resolve the
473  // symbols anymore. We skip under valgrind.
474  if (!RUNNING_ON_VALGRIND) {
475 #endif
476  dlclose(library_handle_);
477  library_handle_ = NULL;
478 #ifdef HAS_VALGRIND_HEADERS
479  }
480 #endif
481 }
482 
483 
484 static CvmfsExports *LoadLibrary(const bool debug_mode,
485  LoaderExports *loader_exports)
486 {
487  std::string local_lib_path = "./";
488  if (getenv("CVMFS_LIBRARY_PATH") != NULL) {
489  local_lib_path = getenv("CVMFS_LIBRARY_PATH");
490  if (!local_lib_path.empty() && (*local_lib_path.rbegin() != '/'))
491  local_lib_path.push_back('/');
492  }
493 
494 #if CVMFS_USE_LIBFUSE == 2
495  string library_name = string("cvmfs_fuse") + ((debug_mode) ? "_debug" : "");
496 #else
497  string library_name = string("cvmfs_fuse3") + ((debug_mode) ? "_debug" : "");
498 #endif
499  library_name = platform_libname(library_name);
500  string error_messages;
501 
502  static vector<string> library_paths; // TODO(rmeusel): C++11 initializer
503  if (library_paths.empty()) {
504  library_paths.push_back(local_lib_path + library_name);
505  library_paths.push_back("/usr/lib/" + library_name);
506  library_paths.push_back("/usr/lib64/" + library_name);
507 #ifdef __APPLE__
508  // Since OS X El Capitan (10.11) came with SIP, we needed to relocate our
509  // binaries from /usr/... to /usr/local/...
510  library_paths.push_back("/usr/local/lib/" + library_name);
511 #endif
512  }
513 
514  vector<string>::const_iterator i = library_paths.begin();
515  vector<string>::const_iterator iend = library_paths.end();
516  for (; i != iend; ++i) { // TODO(rmeusel): C++11 range based for
518  if (library_handle_ != NULL) {
519  break;
520  }
521 
522  error_messages += string(dlerror()) + "\n";
523  }
524 
525  if (!library_handle_) {
527  "failed to load cvmfs library, tried: '%s'\n%s",
528  JoinStrings(library_paths, "' '").c_str(), error_messages.c_str());
529  return NULL;
530  }
531 
532  CvmfsExports **exports_ptr = reinterpret_cast<CvmfsExports **>(
533  dlsym(library_handle_, "g_cvmfs_exports"));
534  if (!exports_ptr)
535  return NULL;
536 
537  if (loader_exports) {
538  LoadEvent *load_event = new LoadEvent();
539  load_event->timestamp = time(NULL);
540  load_event->so_version = (*exports_ptr)->so_version;
541  loader_exports->history.push_back(load_event);
542  }
543 
544  return *exports_ptr;
545 }
546 
547 
548 Failures Reload(const int fd_progress, const bool stop_and_go) {
549  int retval;
550 
551  retval = cvmfs_exports_->fnMaintenanceMode(fd_progress);
552  if (!retval)
553  return kFailMaintenanceMode;
554 
555  SendMsg2Socket(fd_progress, "Blocking new file system calls\n");
556  fence_reload_->Close();
557 
558  SendMsg2Socket(fd_progress, "Waiting for active file system calls\n");
559  fence_reload_->Drain();
560 
561  retval = cvmfs_exports_->fnSaveState(fd_progress,
563  if (!retval)
564  return kFailSaveState;
565 
566  SendMsg2Socket(fd_progress, "Unloading Fuse module\n");
568  CloseLibrary();
569 
570  if (stop_and_go) {
571  CreateFile(*socket_path_ + ".paused", 0600);
572  SendMsg2Socket(fd_progress, "Waiting for the delivery of SIGUSR1...\n");
573  WaitForSignal(SIGUSR1);
574  unlink((*socket_path_ + ".paused").c_str());
575  }
576 
577  SendMsg2Socket(fd_progress, "Re-Loading Fuse module\n");
579  if (!cvmfs_exports_)
580  return kFailLoadLibrary;
582  if (retval != kFailOk) {
583  string msg_progress = cvmfs_exports_->fnGetErrorMsg() + " (" +
584  StringifyInt(retval) + ")\n";
585  LogCvmfs(kLogCvmfs, kLogSyslogErr, "%s", msg_progress.c_str());
586  SendMsg2Socket(fd_progress, msg_progress);
587  return (Failures)retval;
588  }
589 
590  retval = cvmfs_exports_->fnRestoreState(fd_progress,
592  if (!retval)
593  return kFailRestoreState;
595  for (unsigned i = 0, l = loader_exports_->saved_states.size(); i < l; ++i) {
596  delete loader_exports_->saved_states[i];
597  }
598  loader_exports_->saved_states.clear();
599 
600  SendMsg2Socket(fd_progress, "Activating Fuse module\n");
602 
603  fence_reload_->Open();
604  return kFailOk;
605 }
606 
607 } // namespace loader
608 
609 
610 using namespace loader; // NOLINT(build/namespaces)
611 
612 // Making OpenSSL (libcrypto) thread-safe
613 #ifndef OPENSSL_API_INTERFACE_V11
614 
615 pthread_mutex_t *gLibcryptoLocks;
616 
617 static void CallbackLibcryptoLock(int mode, int type,
618  const char *file, int line) {
619  (void)file;
620  (void)line;
621 
622  int retval;
623 
624  if (mode & CRYPTO_LOCK) {
625  retval = pthread_mutex_lock(&(gLibcryptoLocks[type]));
626  } else {
627  retval = pthread_mutex_unlock(&(gLibcryptoLocks[type]));
628  }
629  assert(retval == 0);
630 }
631 
632 static unsigned long CallbackLibcryptoThreadId() { // NOLINT(runtime/int)
633  return platform_gettid();
634 }
635 
636 #endif
637 
638 static void SetupLibcryptoMt() {
639 #ifndef OPENSSL_API_INTERFACE_V11
640  gLibcryptoLocks = static_cast<pthread_mutex_t *>(OPENSSL_malloc(
641  CRYPTO_num_locks() * sizeof(pthread_mutex_t)));
642  for (int i = 0; i < CRYPTO_num_locks(); ++i) {
643  int retval = pthread_mutex_init(&(gLibcryptoLocks[i]), NULL);
644  assert(retval == 0);
645  }
646 
647  CRYPTO_set_id_callback(CallbackLibcryptoThreadId);
648  CRYPTO_set_locking_callback(CallbackLibcryptoLock);
649 #endif
650 }
651 
652 static void CleanupLibcryptoMt(void) {
653 #ifndef OPENSSL_API_INTERFACE_V11
654  CRYPTO_set_locking_callback(NULL);
655  for (int i = 0; i < CRYPTO_num_locks(); ++i)
656  pthread_mutex_destroy(&(gLibcryptoLocks[i]));
657 
658  OPENSSL_free(gLibcryptoLocks);
659 #endif
660 }
661 
662 
663 int FuseMain(int argc, char *argv[]) {
664  // Set a decent umask for new files (no write access to group/everyone).
665  // We want to allow group write access for the talk-socket.
666  umask(007);
667  // SIGUSR1 is used for the stop_and_go mode during reload
668  BlockSignal(SIGUSR1);
669 
670  int retval;
671 
672  // Jump into alternative process flavors (e.g. shared cache manager)
673  // We are here due to a fork+execve (ManagedExec in util.cc) or due to
674  // utility calls of cvmfs2
675  if ((argc > 1) && (strstr(argv[1], "__") == argv[1])) {
676  if (string(argv[1]) == string("__RELOAD__")) {
677  if (argc < 3)
678  return 1;
679  bool stop_and_go = false;
680  if ((argc > 3) && (string(argv[3]) == "stop_and_go"))
681  stop_and_go = true;
682  retval = loader_talk::MainReload(argv[2], stop_and_go);
683  if ((retval != 0) && (stop_and_go)) {
684  CreateFile(string(argv[2]) + ".paused.crashed", 0600);
685  }
686  return retval;
687  }
688 
689  if (string(argv[1]) == string("__MK_ALIEN_CACHE__")) {
690  if (argc < 5)
691  return 1;
692  string alien_cache_dir = argv[2];
694  if (!sanitizer.IsValid(argv[3]) || !sanitizer.IsValid(argv[4]))
695  return 1;
696  uid_t uid_owner = String2Uint64(argv[3]);
697  gid_t gid_owner = String2Uint64(argv[4]);
698 
699  int retval = MkdirDeep(alien_cache_dir, 0770);
700  if (!retval) {
701  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create %s",
702  alien_cache_dir.c_str());
703  return 1;
704  }
705  retval = chown(alien_cache_dir.c_str(), uid_owner, gid_owner);
706  if (retval != 0) {
707  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set owner of %s to %d:%d",
708  alien_cache_dir.c_str(), uid_owner, gid_owner);
709  return 1;
710  }
711  retval = SwitchCredentials(uid_owner, gid_owner, false);
712  if (!retval) {
713  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to impersonate %d:%d",
714  uid_owner, gid_owner);
715  return 1;
716  }
717  // Allow access to user and group
718  retval = MakeCacheDirectories(alien_cache_dir, 0770);
719  if (!retval) {
720  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create cache skeleton");
721  return 1;
722  }
723  return 0;
724  }
725 
726  debug_mode_ = getenv("__CVMFS_DEBUG_MODE__") != NULL;
728  if (!cvmfs_exports_)
729  return kFailLoadLibrary;
730  return cvmfs_exports_->fnAltProcessFlavor(argc, argv);
731  }
732 
734 
735  // Option parsing
736  struct fuse_args *mount_options;
737  mount_options = ParseCmdLine(argc, argv);
738  if (!mount_options) {
739  Usage(argv[0]);
740  return kFailOptions;
741  }
742 
743  string parameter;
744  OptionsManager *options_manager;
746  options_manager = new SimpleOptionsParser(
748  } else {
749  options_manager = new BashOptionsManager(
751  }
752  if (config_files_) {
753  vector<string> tokens = SplitString(*config_files_, ':');
754  for (unsigned i = 0, s = tokens.size(); i < s; ++i) {
755  options_manager->ParsePath(tokens[i], false);
756  }
757  } else {
758  options_manager->ParseDefault(*repository_name_);
759  }
760 
761 #ifdef __APPLE__
762  string volname = "-ovolname=" + *repository_name_;
763  fuse_opt_add_arg(mount_options, volname.c_str());
764  // Allow for up to 5 minute "hangs" before OS X may kill cvmfs
765  fuse_opt_add_arg(mount_options, "-odaemon_timeout=300");
766  fuse_opt_add_arg(mount_options, "-onoapplexattr");
767  // Should libfuse be single-threaded? See CVM-871, CVM-855
768  // single_threaded_ = true;
769 #endif
770  if (options_manager->GetValue("CVMFS_MOUNT_RW", &parameter) &&
771  options_manager->IsOn(parameter))
772  {
773  fuse_opt_add_arg(mount_options, "-orw");
774  } else {
775  fuse_opt_add_arg(mount_options, "-oro");
776  }
777  fuse_opt_add_arg(mount_options, "-onodev");
778  if (options_manager->GetValue("CVMFS_SUID", &parameter) &&
779  options_manager->IsOn(parameter))
780  {
781  suid_mode_ = true;
782  }
783  if (suid_mode_) {
784  if (getuid() != 0) {
786  "must be root to mount with suid option");
787  }
788  fuse_opt_add_arg(mount_options, "-osuid");
789  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with suid support");
790  }
792  loader_exports_->loader_version = PACKAGE_VERSION;
793  loader_exports_->boot_time = time(NULL);
794  loader_exports_->program_name = argv[0];
798  loader_exports_->device_id = "0:0"; // initially unknown, set after mount
801  if (config_files_)
803  else
805 
806  if (parse_options_only_) {
807  LogCvmfs(kLogCvmfs, kLogStdout, "# CernVM-FS parameters:\n%s",
808  options_manager->Dump().c_str());
809  return 0;
810  }
811 
812  // Logging
813  if (options_manager->GetValue("CVMFS_SYSLOG_LEVEL", &parameter))
814  SetLogSyslogLevel(String2Uint64(parameter));
815  else
817  if (options_manager->GetValue("CVMFS_SYSLOG_FACILITY", &parameter))
820  // Deferr setting usyslog until credentials are dropped
821 
822  // Permissions check
823  if (options_manager->GetValue("CVMFS_CHECK_PERMISSIONS", &parameter)) {
824  if (options_manager->IsOn(parameter)) {
825  fuse_opt_add_arg(mount_options, "-odefault_permissions");
826  }
827  }
828 
831  "Moint point %s does not exist", mount_point_->c_str());
832  return kFailPermission;
833  }
834 
835  // Number of file descriptors
836  if (options_manager->GetValue("CVMFS_NFILES", &parameter)) {
837  int retval = SetLimitNoFile(String2Uint64(parameter));
838  if (retval == -2) {
839  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running under valgrind");
840  } else if (retval == -1) {
841  if (system_mount_) {
843  "Failed to set maximum number of open files, "
844  "insufficient permissions");
845  return kFailPermission;
846  }
847  unsigned soft_limit, hard_limit;
848  GetLimitNoFile(&soft_limit, &hard_limit);
850  "Failed to set requested number of open files, "
851  "using maximum number %u", hard_limit);
852  if (hard_limit > soft_limit) {
853  (void) SetLimitNoFile(hard_limit);
854  }
855  }
856  }
857 
858  // Apply OOM score adjustment
859  if (options_manager->GetValue("CVMFS_OOM_SCORE_ADJ", &parameter)) {
860  string proc_path = "/proc/" + StringifyInt(getpid()) + "/oom_score_adj";
861  int fd_oom = open(proc_path.c_str(), O_WRONLY);
862  if (fd_oom < 0) {
864  "failed to open %s", proc_path.c_str());
865  } else {
866  bool retval = SafeWrite(fd_oom, parameter.data(), parameter.length());
867  if (!retval) {
869  "failed to set OOM score adjustment to %s", parameter.c_str());
870  }
871  close(fd_oom);
872  }
873  }
874 
875  // Protect the process from being killed by systemd
876  if (options_manager->GetValue("CVMFS_SYSTEMD_NOKILL", &parameter) &&
877  options_manager->IsOn(parameter))
878  {
879  argv[0][0] = '@';
880  }
881 
882  // Grab mountpoint
883  if (grab_mountpoint_) {
884  if ((chown(mount_point_->c_str(), uid_, gid_) != 0) ||
885  (chmod(mount_point_->c_str(), 0755) != 0))
886  {
888  "Failed to grab mountpoint %s (%d)",
889  mount_point_->c_str(), errno);
890  return kFailPermission;
891  }
892  }
893 
894  // Drop credentials
895  if ((uid_ != 0) || (gid_ != 0)) {
896  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with credentials %d:%d",
897  uid_, gid_);
898  const bool retrievable = (suid_mode_ || !disable_watchdog_);
899  if (!SwitchCredentials(uid_, gid_, retrievable)) {
901  "Failed to drop credentials");
902  return kFailPermission;
903  }
904  }
905 
906  // Only set usyslog now, otherwise file permissions are wrong
907  usyslog_path_ = new string();
908  if (options_manager->GetValue("CVMFS_USYSLOG", &parameter))
909  *usyslog_path_ = parameter;
911 
912  if (single_threaded_) {
914  "CernVM-FS: running in single threaded mode");
915  }
916  if (debug_mode_) {
918  "CernVM-FS: running in debug mode");
919  }
920 
921 #ifndef FUSE_CAP_POSIX_ACL
922  if (options_manager->GetValue("CVMFS_ENFORCE_ACLS", &parameter) &&
923  options_manager->IsOn(parameter))
924  {
926  "CernVM-FS: ACL support requested but not available in this "
927  "version of libfuse");
928  return kFailPermission;
929  }
930 #endif
931 
932  // Initialize the loader socket, connections are not accepted until Spawn()
933  socket_path_ = new string("/var/run/cvmfs");
934  if (options_manager->GetValue("CVMFS_RELOAD_SOCKETS", &parameter))
935  *socket_path_ = MakeCanonicalPath(parameter);
936  *socket_path_ += "/cvmfs." + *repository_name_;
937  retval = loader_talk::Init(*socket_path_);
938  if (!retval) {
940  "Failed to initialize loader socket");
941  return kFailLoaderTalk;
942  }
943 
944  // Options are not needed anymore
945  delete options_manager;
946  options_manager = NULL;
947 
948  struct fuse_session *session;
949 #if CVMFS_USE_LIBFUSE == 2
950  struct fuse_chan *channel;
951  loader_exports_->fuse_channel_or_session = reinterpret_cast<void **>(
952  &channel);
953 #else
954  loader_exports_->fuse_channel_or_session = reinterpret_cast<void **>(
955  &session);
956 #endif
957 
958  // Load and initialize cvmfs library
960  "CernVM-FS: loading Fuse module... ");
962  if (!cvmfs_exports_) {
963  return kFailLoadLibrary;
964  }
966  if (retval != kFailOk) {
967  if (retval == kFailDoubleMount) {
969  "\nCernVM-FS: repository %s already mounted on %s",
971  loader_exports_->mount_point.c_str());
972  return 0;
973  }
974  LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "%s (%d - %s)",
975  cvmfs_exports_->fnGetErrorMsg().c_str(),
976  retval, Code2Ascii((Failures)retval));
978  return retval;
979  }
980  LogCvmfs(kLogCvmfs, kLogStdout, "done");
981 
982  // Mount
983  fence_reload_ = new Fence();
984 
985  if (suid_mode_) {
986  const bool retrievable = true;
987  if (!SwitchCredentials(0, getgid(), retrievable)) {
989  "failed to re-gain root permissions for mounting");
991  return kFailPermission;
992  }
993  }
994 
995 
996  struct fuse_lowlevel_ops loader_operations;
997  SetFuseOperations(&loader_operations);
998 #if (FUSE_VERSION >= 29)
999  if (cvmfs_exports_->cvmfs_operations.forget_multi)
1000  loader_operations.forget_multi = stub_forget_multi;
1001 #endif
1002 
1003 #if CVMFS_USE_LIBFUSE == 2
1004  channel = fuse_mount(mount_point_->c_str(), mount_options);
1005  if (!channel) {
1007  "failed to create Fuse channel");
1009  return kFailMount;
1010  }
1011 
1012  session = fuse_lowlevel_new(mount_options, &loader_operations,
1013  sizeof(loader_operations), NULL);
1014  if (!session) {
1016  "failed to create Fuse session");
1017  fuse_unmount(mount_point_->c_str(), channel);
1019  return kFailMount;
1020  }
1021 #else
1022  // libfuse3
1023  session = fuse_session_new(mount_options, &loader_operations,
1024  sizeof(loader_operations), NULL);
1025  if (!session) {
1027  "failed to create Fuse session");
1029  return kFailMount;
1030  }
1031  retval = fuse_session_mount(session, mount_point_->c_str());
1032  if (retval != 0) {
1034  "failed to mount file system");
1036  return kFailMount;
1037  }
1038 #endif
1039 
1040  // drop credentials
1041  if (suid_mode_) {
1042  const bool retrievable = !disable_watchdog_;
1043  if (!SwitchCredentials(uid_, gid_, retrievable)) {
1045  "failed to drop permissions after mounting");
1047  return kFailPermission;
1048  }
1049  }
1050 
1051  // Determine device id
1052  int fd_mountinfo = open("/proc/self/mountinfo", O_RDONLY);
1053  if (fd_mountinfo > 0) {
1054  std::string line;
1055  while (GetLineFd(fd_mountinfo, &line)) {
1056  std::vector<std::string> tokens = SplitString(line, ' ');
1057  if (tokens.size() < 5) continue;
1058  if (tokens[4] != loader_exports_->mount_point) continue;
1059  unsigned i = 5;
1060  for (; i < tokens.size(); ++i) {
1061  if (tokens[i] == "-") break;
1062  }
1063  if (tokens.size() < i + 3) continue;
1064  if (tokens[i + 2] != "cvmfs2") continue;
1065  loader_exports_->device_id = tokens[2];
1066  break;
1067  }
1068  close(fd_mountinfo);
1069  }
1070 
1071  if (!premounted_) {
1072  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: mounted cvmfs on %s",
1073  mount_point_->c_str());
1074  }
1076  "CernVM-FS: linking %s to repository %s",
1077  mount_point_->c_str(), repository_name_->c_str());
1078  if (!foreground_)
1079  Daemonize();
1080 
1083 
1084  SetLogMicroSyslog("");
1085  retval = fuse_set_signal_handlers(session);
1086  assert(retval == 0);
1087 #if CVMFS_USE_LIBFUSE == 2
1088  fuse_session_add_chan(session, channel);
1089 #endif
1090  if (single_threaded_) {
1091  retval = fuse_session_loop(session);
1092  } else {
1093 #if CVMFS_USE_LIBFUSE == 2
1094  retval = fuse_session_loop_mt(session);
1095 #else
1096  retval = fuse_session_loop_mt(session, 1 /* use fd per thread */);
1097 #endif
1098  }
1100 
1103 
1104  // Unmount
1105 #if CVMFS_USE_LIBFUSE == 2
1106  fuse_remove_signal_handlers(session);
1107  fuse_session_remove_chan(channel);
1108  fuse_session_destroy(session);
1109  fuse_unmount(mount_point_->c_str(), channel);
1110  channel = NULL;
1111 #else
1112  // libfuse3
1113  fuse_remove_signal_handlers(session);
1114  fuse_session_unmount(session);
1115  fuse_session_destroy(session);
1116 #endif
1117  fuse_opt_free_args(mount_options);
1118  delete mount_options;
1119  session = NULL;
1120  mount_options = NULL;
1121 
1122  CloseLibrary();
1123 
1124  LogCvmfs(kLogCvmfs, kLogSyslog, "CernVM-FS: unmounted %s (%s)",
1125  mount_point_->c_str(), repository_name_->c_str());
1126 
1128 
1129  delete fence_reload_;
1130  delete loader_exports_;
1131  delete config_files_;
1132  delete repository_name_;
1133  delete mount_point_;
1134  delete socket_path_;
1135  fence_reload_ = NULL;
1136  loader_exports_ = NULL;
1137  config_files_ = NULL;
1138  repository_name_ = NULL;
1139  mount_point_ = NULL;
1140  socket_path_ = NULL;
1141 
1142  if (retval != 0)
1143  return kFailFuseLoop;
1144  return kFailOk;
1145 }
1146 
1147 
1148 __attribute__((visibility("default")))
1149 CvmfsStubExports *g_cvmfs_stub_exports = NULL;
1150 
1151 static void __attribute__((constructor)) LibraryMain() {
1152  g_cvmfs_stub_exports = new CvmfsStubExports();
1153  g_cvmfs_stub_exports->fn_main = FuseMain;
1154 }
1155 
1156 static void __attribute__((destructor)) LibraryExit() {
1157  delete g_cvmfs_stub_exports;
1158  g_cvmfs_stub_exports = NULL;
1159 }
bool MakeCacheDirectories(const std::string &path, const mode_t mode)
Definition: posix.cc:907
bool simple_options_parsing_
Definition: loader.cc:143
std::string repository_name
Definition: loader.h:176
Failures Reload(const int fd_progress, const bool stop_and_go)
Definition: loader.cc:548
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
bool debug_mode_
Definition: loader.cc:136
static void stub_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: loader.cc:237
void SetLogSyslogFacility(const int local_facility)
Definition: logging.cc:129
static void stub_destroy(void *userdata)
Definition: loader.cc:209
const char * Code2Ascii(const ObjectFetcherFailures::Failures error)
std::string mount_point
Definition: loader.h:177
void SetLogSyslogLevel(const int level)
Definition: logging.cc:97
static void stub_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
Definition: loader.cc:308
time_t timestamp
Definition: loader.h:136
static unsigned long CallbackLibcryptoThreadId()
Definition: loader.cc:632
bool GetValue(const std::string &key, std::string *value)
Definition: options.cc:376
virtual void ParsePath(const std::string &config_file, const bool external)=0
Session * session() const
Definition: repository.h:309
pthread_t platform_gettid()
static struct fuse_opt cvmfs_array_opts[]
Definition: loader.cc:92
EventList history
Definition: loader.h:180
bool grab_mountpoint_
Definition: loader.cc:138
static CvmfsExports * LoadLibrary(const bool debug_mode, LoaderExports *loader_exports)
Definition: loader.cc:484
vector< string > SplitString(const string &str, const char delim, const unsigned max_chunks)
Definition: string.cc:288
static void stub_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
Definition: loader.cc:269
int simple_options_parsing
Definition: loader.cc:67
int(* fnAltProcessFlavor)(int argc, char **argv)
Definition: loader.h:231
int FuseMain(int argc, char *argv[])
Definition: loader.cc:663
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
Definition: posix.cc:313
#define PANIC(...)
Definition: exception.h:26
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:318
string * usyslog_path_
Definition: loader.cc:131
gid_t gid_
Definition: loader.cc:133
std::string so_version
Definition: loader.h:137
static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
Definition: loader.cc:295
bool foreground_
Definition: loader.cc:135
void(* fnFini)()
Definition: loader.h:234
void Daemonize()
Definition: posix.cc:1637
string * config_files_
Definition: loader.cc:129
Fence * fence_reload_
Definition: loader.cc:145
bool SafeWrite(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:1924
void SendMsg2Socket(const int fd, const std::string &msg)
Definition: posix.cc:692
static void CallbackLibcryptoLock(int mode, int type, const char *file, int line)
Definition: loader.cc:617
static void CleanupLibcryptoMt(void)
Definition: loader.cc:652
assert((mem||(size==0))&&"Out Of Memory")
void ** fuse_channel_or_session
Definition: loader.h:194
MountPoint * mount_point_
Definition: cvmfs.cc:121
std::string program_name
Definition: loader.h:179
bool disable_watchdog_
Definition: loader.cc:142
std::string loader_version
Definition: loader.h:174
void SetLogMicroSyslog(const std::string &filename)
Definition: logging.cc:214
void ParseDefault(const std::string &fqrn)
Definition: options.cc:282
bool(* fnSaveState)(const int fd_progress, StateList *saved_states)
Definition: loader.h:237
bool parse_options_only_
Definition: loader.cc:139
struct cvmcache_object_info __attribute__
Definition: atomic.h:24
string * socket_path_
Definition: loader.cc:130
static void stub_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
Definition: loader.cc:253
int SetLimitNoFile(unsigned limit_nofile)
Definition: posix.cc:1473
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:114
static void stub_statfs(fuse_req_t req, fuse_ino_t ino)
Definition: loader.cc:285
int64_t String2Int64(const string &value)
Definition: string.cc:222
void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit)
Definition: posix.cc:1495
uid_t uid_
Definition: loader.cc:132
static void stub_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: loader.cc:261
static void stub_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: loader.cc:245
static void stub_readlink(fuse_req_t req, fuse_ino_t ino)
Definition: loader.cc:231
static void stub_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
Definition: loader.cc:215
bool CheckPremounted(const std::string &mountpoint)
Definition: loader.cc:188
std::string Dump()
Definition: options.cc:447
bool(* fnRestoreState)(const int fd_progress, const StateList &saved_states)
Definition: loader.h:238
#define CVMFS_OPT(t, p, v)
Definition: loader.cc:90
static fuse_args * ParseCmdLine(int argc, char *argv[])
Definition: loader.cc:406
const loader::LoaderExports * loader_exports_
Definition: cvmfs.cc:144
std::string config_files
Definition: loader.h:178
static void SetupLibcryptoMt()
Definition: loader.cc:638
struct fuse_lowlevel_ops cvmfs_operations
Definition: loader.h:241
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
Definition: posix.cc:871
void Drain()
Definition: fence.h:52
pthread_mutex_t * gLibcryptoLocks
Definition: loader.cc:615
void WaitForSignal(int signum)
Definition: posix.cc:1603
int MainReload(const std::string &socket_path, const bool stop_and_go)
Definition: loader_talk.cc:114
static void SetFuseOperations(struct fuse_lowlevel_ops *loader_operations)
Definition: loader.cc:443
void Usage()
int(* fnInit)(const LoaderExports *loader_exports)
Definition: loader.h:232
bool simple_options_parsing
Definition: loader.h:187
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:1755
static void stub_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
Definition: loader.cc:314
bool system_mount_
Definition: loader.cc:137
bool(* fnMaintenanceMode)(const int fd_progress)
Definition: loader.h:236
void(* fnFreeSavedState)(const int fd_progress, const StateList &saved_states)
Definition: loader.h:239
string StringifyInt(const int64_t value)
Definition: string.cc:78
Failures
Definition: loader.h:27
std::string device_id
Definition: loader.h:199
std::string platform_libname(const std::string &base_name)
bool DirectoryExists(const std::string &path)
Definition: posix.cc:838
static void stub_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: loader.cc:223
void * library_handle_
Definition: loader.cc:144
void(* fnSpawn)()
Definition: loader.h:233
bool GetLineFd(const int fd, std::string *line)
Definition: string.cc:397
static void CloseLibrary()
Definition: loader.cc:470
uint64_t String2Uint64(const string &value)
Definition: string.cc:228
std::string(* fnGetErrorMsg)()
Definition: loader.h:235
static void Fini()
Definition: cvmfs.cc:1961
static void Spawn()
Definition: cvmfs.cc:1876
bool SwitchCredentials(const uid_t uid, const gid_t gid, const bool temporarily)
Definition: posix.cc:786
static int ParseFuseOptions(void *data __attribute__((unused)), const char *arg, int key, struct fuse_args *outargs)
Definition: loader.cc:346
Definition: fence.h:25
bool premounted_
Definition: loader.cc:141
StateList saved_states
Definition: loader.h:181
bool suid_mode_
Definition: loader.cc:140
static void stub_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: loader.cc:277
void Close()
Definition: fence.h:45
#define CVMFS_SWITCH(t, p)
Definition: loader.cc:91
void SetLogSyslogPrefix(const std::string &prefix)
Definition: logging.cc:187
std::string MakeCanonicalPath(const std::string &path)
Definition: posix.cc:96
static void stub_init(void *userdata, struct fuse_conn_info *conn)
Definition: loader.cc:203
static void size_t size
Definition: smalloc.h:47
bool single_threaded_
Definition: loader.cc:134
bool IsOn(const std::string &param_value)
Definition: options.cc:409
CvmfsExports * cvmfs_exports_
Definition: loader.cc:146
static void * OpenLibrary(const string &path)
Definition: loader.cc:465
void BlockSignal(int signum)
Definition: posix.cc:1588
void Open()
Definition: fence.h:59
std::string * repository_name_
Definition: loader.cc:127