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