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 
404 static fuse_args *ParseCmdLine(int argc, char *argv[]) {
405  struct fuse_args *mount_options = new fuse_args();
407  memset(&cvmfs_options, 0, sizeof(cvmfs_options));
408 
409  mount_options->argc = argc;
410  mount_options->argv = argv;
411  mount_options->allocated = 0;
412  if ((fuse_opt_parse(mount_options, &cvmfs_options, cvmfs_array_opts,
413  ParseFuseOptions) != 0) ||
415  {
416  delete mount_options;
417  return NULL;
418  }
419  if (cvmfs_options.config) {
420  config_files_ = new string(cvmfs_options.config);
421  free(cvmfs_options.config);
422  }
423  uid_ = cvmfs_options.uid;
424  gid_ = cvmfs_options.gid;
425  system_mount_ = cvmfs_options.system_mount;
426  grab_mountpoint_ = cvmfs_options.grab_mountpoint;
427  suid_mode_ = cvmfs_options.cvmfs_suid;
428  disable_watchdog_ = cvmfs_options.disable_watchdog;
430  if (cvmfs_options.foreground) {
431  foreground_ = true;
432  }
433  if (cvmfs_options.fuse_debug) {
434  fuse_opt_add_arg(mount_options, "-d");
435  }
436 
437  return mount_options;
438 }
439 
440 
441 static void SetFuseOperations(struct fuse_lowlevel_ops *loader_operations) {
442  memset(loader_operations, 0, sizeof(*loader_operations));
443 
444  loader_operations->init = stub_init;
445  loader_operations->destroy = stub_destroy;
446 
447  loader_operations->lookup = stub_lookup;
448  loader_operations->getattr = stub_getattr;
449  loader_operations->readlink = stub_readlink;
450  loader_operations->open = stub_open;
451  loader_operations->read = stub_read;
452  loader_operations->release = stub_release;
453  loader_operations->opendir = stub_opendir;
454  loader_operations->readdir = stub_readdir;
455  loader_operations->releasedir = stub_releasedir;
456  loader_operations->statfs = stub_statfs;
457  loader_operations->getxattr = stub_getxattr;
458  loader_operations->listxattr = stub_listxattr;
459  loader_operations->forget = stub_forget;
460 }
461 
462 
463 static void *OpenLibrary(const string &path) {
464  return dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
465 }
466 
467 
468 static void CloseLibrary() {
469 #ifdef HAS_VALGRIND_HEADERS
470  // If the libcvmfs_fuse library is unloaded, valgrind can't resolve the
471  // symbols anymore. We skip under valgrind.
472  if (!RUNNING_ON_VALGRIND) {
473 #endif
474  dlclose(library_handle_);
475  library_handle_ = NULL;
476 #ifdef HAS_VALGRIND_HEADERS
477  }
478 #endif
479 }
480 
481 
482 static CvmfsExports *LoadLibrary(const bool debug_mode,
483  LoaderExports *loader_exports)
484 {
485  std::string local_lib_path = "./";
486  if (getenv("CVMFS_LIBRARY_PATH") != NULL) {
487  local_lib_path = getenv("CVMFS_LIBRARY_PATH");
488  if (!local_lib_path.empty() && (*local_lib_path.rbegin() != '/'))
489  local_lib_path.push_back('/');
490  }
491 
492 #if CVMFS_USE_LIBFUSE == 2
493  string library_name = string("cvmfs_fuse") + ((debug_mode) ? "_debug" : "");
494 #else
495  string library_name = string("cvmfs_fuse3") + ((debug_mode) ? "_debug" : "");
496 #endif
497  library_name = platform_libname(library_name);
498  string error_messages;
499 
500  static vector<string> library_paths; // TODO(rmeusel): C++11 initializer
501  if (library_paths.empty()) {
502  library_paths.push_back(local_lib_path + library_name);
503  library_paths.push_back("/usr/lib/" + library_name);
504  library_paths.push_back("/usr/lib64/" + library_name);
505 #ifdef __APPLE__
506  // Since OS X El Capitan (10.11) came with SIP, we needed to relocate our
507  // binaries from /usr/... to /usr/local/...
508  library_paths.push_back("/usr/local/lib/" + library_name);
509 #endif
510  }
511 
512  vector<string>::const_iterator i = library_paths.begin();
513  vector<string>::const_iterator iend = library_paths.end();
514  for (; i != iend; ++i) { // TODO(rmeusel): C++11 range based for
516  if (library_handle_ != NULL) {
517  break;
518  }
519 
520  error_messages += string(dlerror()) + "\n";
521  }
522 
523  if (!library_handle_) {
525  "failed to load cvmfs library, tried: '%s'\n%s",
526  JoinStrings(library_paths, "' '").c_str(), error_messages.c_str());
527  return NULL;
528  }
529 
530  CvmfsExports **exports_ptr = reinterpret_cast<CvmfsExports **>(
531  dlsym(library_handle_, "g_cvmfs_exports"));
532  if (!exports_ptr)
533  return NULL;
534 
535  if (loader_exports) {
536  LoadEvent *load_event = new LoadEvent();
537  load_event->timestamp = time(NULL);
538  load_event->so_version = (*exports_ptr)->so_version;
539  loader_exports->history.push_back(load_event);
540  }
541 
542  return *exports_ptr;
543 }
544 
545 
546 Failures Reload(const int fd_progress, const bool stop_and_go) {
547  int retval;
548 
549  retval = cvmfs_exports_->fnMaintenanceMode(fd_progress);
550  if (!retval)
551  return kFailMaintenanceMode;
552 
553  SendMsg2Socket(fd_progress, "Blocking new file system calls\n");
554  fence_reload_->Close();
555 
556  SendMsg2Socket(fd_progress, "Waiting for active file system calls\n");
557  fence_reload_->Drain();
558 
559  retval = cvmfs_exports_->fnSaveState(fd_progress,
561  if (!retval)
562  return kFailSaveState;
563 
564  SendMsg2Socket(fd_progress, "Unloading Fuse module\n");
566  CloseLibrary();
567 
568  if (stop_and_go) {
569  CreateFile(*socket_path_ + ".paused", 0600);
570  SendMsg2Socket(fd_progress, "Waiting for the delivery of SIGUSR1...\n");
571  WaitForSignal(SIGUSR1);
572  unlink((*socket_path_ + ".paused").c_str());
573  }
574 
575  SendMsg2Socket(fd_progress, "Re-Loading Fuse module\n");
577  if (!cvmfs_exports_)
578  return kFailLoadLibrary;
580  if (retval != kFailOk) {
581  string msg_progress = cvmfs_exports_->fnGetErrorMsg() + " (" +
582  StringifyInt(retval) + ")\n";
583  LogCvmfs(kLogCvmfs, kLogSyslogErr, "%s", msg_progress.c_str());
584  SendMsg2Socket(fd_progress, msg_progress);
585  return (Failures)retval;
586  }
587 
588  retval = cvmfs_exports_->fnRestoreState(fd_progress,
590  if (!retval)
591  return kFailRestoreState;
593  for (unsigned i = 0, l = loader_exports_->saved_states.size(); i < l; ++i) {
594  delete loader_exports_->saved_states[i];
595  }
596  loader_exports_->saved_states.clear();
597 
598  SendMsg2Socket(fd_progress, "Activating Fuse module\n");
600 
601  fence_reload_->Open();
602  return kFailOk;
603 }
604 
605 } // namespace loader
606 
607 
608 using namespace loader; // NOLINT(build/namespaces)
609 
610 int FuseMain(int argc, char *argv[]) {
611  // Set a decent umask for new files (no write access to group/everyone).
612  // We want to allow group write access for the talk-socket.
613  umask(007);
614  // SIGUSR1 is used for the stop_and_go mode during reload
615  BlockSignal(SIGUSR1);
616 
617  int retval;
618 
619  // Jump into alternative process flavors (e.g. shared cache manager)
620  // We are here due to a fork+execve (ManagedExec in util.cc) or due to
621  // utility calls of cvmfs2
622  if ((argc > 1) && (strstr(argv[1], "__") == argv[1])) {
623  if (string(argv[1]) == string("__RELOAD__")) {
624  if (argc < 3)
625  return 1;
626  bool stop_and_go = false;
627  if ((argc > 3) && (string(argv[3]) == "stop_and_go"))
628  stop_and_go = true;
629  retval = loader_talk::MainReload(argv[2], stop_and_go);
630  if ((retval != 0) && (stop_and_go)) {
631  CreateFile(string(argv[2]) + ".paused.crashed", 0600);
632  }
633  return retval;
634  }
635 
636  if (string(argv[1]) == string("__MK_ALIEN_CACHE__")) {
637  if (argc < 5)
638  return 1;
639  string alien_cache_dir = argv[2];
641  if (!sanitizer.IsValid(argv[3]) || !sanitizer.IsValid(argv[4]))
642  return 1;
643  uid_t uid_owner = String2Uint64(argv[3]);
644  gid_t gid_owner = String2Uint64(argv[4]);
645 
646  int retval = MkdirDeep(alien_cache_dir, 0770);
647  if (!retval) {
648  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create %s",
649  alien_cache_dir.c_str());
650  return 1;
651  }
652  retval = chown(alien_cache_dir.c_str(), uid_owner, gid_owner);
653  if (retval != 0) {
654  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set owner of %s to %d:%d",
655  alien_cache_dir.c_str(), uid_owner, gid_owner);
656  return 1;
657  }
658  retval = SwitchCredentials(uid_owner, gid_owner, false);
659  if (!retval) {
660  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to impersonate %d:%d",
661  uid_owner, gid_owner);
662  return 1;
663  }
664  // Allow access to user and group
665  retval = MakeCacheDirectories(alien_cache_dir, 0770);
666  if (!retval) {
667  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create cache skeleton");
668  return 1;
669  }
670  return 0;
671  }
672 
673  debug_mode_ = getenv("__CVMFS_DEBUG_MODE__") != NULL;
675  if (!cvmfs_exports_)
676  return kFailLoadLibrary;
677  return cvmfs_exports_->fnAltProcessFlavor(argc, argv);
678  }
679 
680  // Option parsing
681  struct fuse_args *mount_options;
682  mount_options = ParseCmdLine(argc, argv);
683  if (!mount_options) {
684  Usage(argv[0]);
685  return kFailOptions;
686  }
687 
688  string parameter;
689  OptionsManager *options_manager;
691  options_manager = new SimpleOptionsParser(
693  } else {
694  options_manager = new BashOptionsManager(
696  }
697  if (config_files_) {
698  vector<string> tokens = SplitString(*config_files_, ':');
699  for (unsigned i = 0, s = tokens.size(); i < s; ++i) {
700  options_manager->ParsePath(tokens[i], false);
701  }
702  } else {
703  options_manager->ParseDefault(*repository_name_);
704  }
705 
706 #ifdef __APPLE__
707  string volname = "-ovolname=" + *repository_name_;
708  fuse_opt_add_arg(mount_options, volname.c_str());
709  // Allow for up to 5 minute "hangs" before OS X may kill cvmfs
710  fuse_opt_add_arg(mount_options, "-odaemon_timeout=300");
711  fuse_opt_add_arg(mount_options, "-onoapplexattr");
712  // Should libfuse be single-threaded? See CVM-871, CVM-855
713  // single_threaded_ = true;
714 #endif
715  if (options_manager->GetValue("CVMFS_MOUNT_RW", &parameter) &&
716  options_manager->IsOn(parameter))
717  {
718  fuse_opt_add_arg(mount_options, "-orw");
719  } else {
720  fuse_opt_add_arg(mount_options, "-oro");
721  }
722  fuse_opt_add_arg(mount_options, "-onodev");
723  if (options_manager->GetValue("CVMFS_SUID", &parameter) &&
724  options_manager->IsOn(parameter))
725  {
726  suid_mode_ = true;
727  }
728  if (suid_mode_) {
729  if (getuid() != 0) {
731  "must be root to mount with suid option");
732  }
733  fuse_opt_add_arg(mount_options, "-osuid");
734  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with suid support");
735  }
737  loader_exports_->loader_version = PACKAGE_VERSION;
738  loader_exports_->boot_time = time(NULL);
739  loader_exports_->program_name = argv[0];
743  loader_exports_->device_id = "0:0"; // initially unknown, set after mount
746  if (config_files_)
748  else
750 
751  if (parse_options_only_) {
752  LogCvmfs(kLogCvmfs, kLogStdout, "# CernVM-FS parameters:\n%s",
753  options_manager->Dump().c_str());
754  return 0;
755  }
756 
757  // Logging
758  if (options_manager->GetValue("CVMFS_SYSLOG_LEVEL", &parameter))
759  SetLogSyslogLevel(String2Uint64(parameter));
760  else
762  if (options_manager->GetValue("CVMFS_SYSLOG_FACILITY", &parameter))
765  // Deferr setting usyslog until credentials are dropped
766 
767  // Permissions check
768  if (options_manager->GetValue("CVMFS_CHECK_PERMISSIONS", &parameter)) {
769  if (options_manager->IsOn(parameter)) {
770  fuse_opt_add_arg(mount_options, "-odefault_permissions");
771  }
772  }
773 
776  "Moint point %s does not exist", mount_point_->c_str());
777  return kFailPermission;
778  }
779 
780  // Number of file descriptors
781  if (options_manager->GetValue("CVMFS_NFILES", &parameter)) {
782  int retval = SetLimitNoFile(String2Uint64(parameter));
783  if (retval == -2) {
784  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running under valgrind");
785  } else if (retval == -1) {
786  if (system_mount_) {
788  "Failed to set maximum number of open files, "
789  "insufficient permissions");
790  return kFailPermission;
791  }
792  unsigned soft_limit, hard_limit;
793  GetLimitNoFile(&soft_limit, &hard_limit);
795  "Failed to set requested number of open files, "
796  "using maximum number %u", hard_limit);
797  if (hard_limit > soft_limit) {
798  (void) SetLimitNoFile(hard_limit);
799  }
800  }
801  }
802 
803  // Apply OOM score adjustment
804  if (options_manager->GetValue("CVMFS_OOM_SCORE_ADJ", &parameter)) {
805  string proc_path = "/proc/" + StringifyInt(getpid()) + "/oom_score_adj";
806  int fd_oom = open(proc_path.c_str(), O_WRONLY);
807  if (fd_oom < 0) {
809  "failed to open %s", proc_path.c_str());
810  } else {
811  bool retval = SafeWrite(fd_oom, parameter.data(), parameter.length());
812  if (!retval) {
814  "failed to set OOM score adjustment to %s", parameter.c_str());
815  }
816  close(fd_oom);
817  }
818  }
819 
820  // Protect the process from being killed by systemd
821  if (options_manager->GetValue("CVMFS_SYSTEMD_NOKILL", &parameter) &&
822  options_manager->IsOn(parameter))
823  {
824  argv[0][0] = '@';
825  }
826 
827  // Grab mountpoint
828  if (grab_mountpoint_) {
829  if ((chown(mount_point_->c_str(), uid_, gid_) != 0) ||
830  (chmod(mount_point_->c_str(), 0755) != 0))
831  {
833  "Failed to grab mountpoint %s (%d)",
834  mount_point_->c_str(), errno);
835  return kFailPermission;
836  }
837  }
838 
839  // Drop credentials
840  if ((uid_ != 0) || (gid_ != 0)) {
841  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with credentials %d:%d",
842  uid_, gid_);
843  const bool retrievable = (suid_mode_ || !disable_watchdog_);
844  if (!SwitchCredentials(uid_, gid_, retrievable)) {
846  "Failed to drop credentials");
847  return kFailPermission;
848  }
849  }
850 
851  // Only set usyslog now, otherwise file permissions are wrong
852  usyslog_path_ = new string();
853  if (options_manager->GetValue("CVMFS_USYSLOG", &parameter))
854  *usyslog_path_ = parameter;
856 
857  if (single_threaded_) {
859  "CernVM-FS: running in single threaded mode");
860  }
861  if (debug_mode_) {
863  "CernVM-FS: running in debug mode");
864  }
865 
866 #ifndef FUSE_CAP_POSIX_ACL
867  if (options_manager->GetValue("CVMFS_ENFORCE_ACLS", &parameter) &&
868  options_manager->IsOn(parameter))
869  {
871  "CernVM-FS: ACL support requested but not available in this "
872  "version of libfuse");
873  return kFailPermission;
874  }
875 #endif
876 
877  // Initialize the loader socket, connections are not accepted until Spawn()
878  socket_path_ = new string("/var/run/cvmfs");
879  if (options_manager->GetValue("CVMFS_RELOAD_SOCKETS", &parameter))
880  *socket_path_ = MakeCanonicalPath(parameter);
881  *socket_path_ += "/cvmfs." + *repository_name_;
882  retval = loader_talk::Init(*socket_path_);
883  if (!retval) {
885  "Failed to initialize loader socket");
886  return kFailLoaderTalk;
887  }
888 
889  // Options are not needed anymore
890  delete options_manager;
891  options_manager = NULL;
892 
893  struct fuse_session *session;
894 #if CVMFS_USE_LIBFUSE == 2
895  struct fuse_chan *channel;
896  loader_exports_->fuse_channel_or_session = reinterpret_cast<void **>(
897  &channel);
898 #else
899  loader_exports_->fuse_channel_or_session = reinterpret_cast<void **>(
900  &session);
901 #endif
902 
903  // Load and initialize cvmfs library
905  "CernVM-FS: loading Fuse module... ");
907  if (!cvmfs_exports_) {
908  return kFailLoadLibrary;
909  }
911  if (retval != kFailOk) {
912  if (retval == kFailDoubleMount) {
914  "\nCernVM-FS: repository %s already mounted on %s",
916  loader_exports_->mount_point.c_str());
917  return 0;
918  }
919  LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "%s (%d - %s)",
920  cvmfs_exports_->fnGetErrorMsg().c_str(),
921  retval, Code2Ascii((Failures)retval));
923  return retval;
924  }
925  LogCvmfs(kLogCvmfs, kLogStdout, "done");
926 
927  // Mount
928  fence_reload_ = new Fence();
929 
930  if (suid_mode_) {
931  const bool retrievable = true;
932  if (!SwitchCredentials(0, getgid(), retrievable)) {
934  "failed to re-gain root permissions for mounting");
936  return kFailPermission;
937  }
938  }
939 
940 
941  struct fuse_lowlevel_ops loader_operations;
942  SetFuseOperations(&loader_operations);
943 #if (FUSE_VERSION >= 29)
944  if (cvmfs_exports_->cvmfs_operations.forget_multi)
945  loader_operations.forget_multi = stub_forget_multi;
946 #endif
947 
948 #if CVMFS_USE_LIBFUSE == 2
949  channel = fuse_mount(mount_point_->c_str(), mount_options);
950  if (!channel) {
952  "failed to create Fuse channel");
954  return kFailMount;
955  }
956 
957  session = fuse_lowlevel_new(mount_options, &loader_operations,
958  sizeof(loader_operations), NULL);
959  if (!session) {
961  "failed to create Fuse session");
962  fuse_unmount(mount_point_->c_str(), channel);
964  return kFailMount;
965  }
966 #else
967  // libfuse3
968  session = fuse_session_new(mount_options, &loader_operations,
969  sizeof(loader_operations), NULL);
970  if (!session) {
972  "failed to create Fuse session");
974  return kFailMount;
975  }
976  retval = fuse_session_mount(session, mount_point_->c_str());
977  if (retval != 0) {
979  "failed to mount file system");
981  return kFailMount;
982  }
983 #endif
984 
985  // drop credentials
986  if (suid_mode_) {
987  const bool retrievable = !disable_watchdog_;
988  if (!SwitchCredentials(uid_, gid_, retrievable)) {
990  "failed to drop permissions after mounting");
992  return kFailPermission;
993  }
994  }
995 
996  // Determine device id
997  int fd_mountinfo = open("/proc/self/mountinfo", O_RDONLY);
998  if (fd_mountinfo > 0) {
999  std::string line;
1000  while (GetLineFd(fd_mountinfo, &line)) {
1001  std::vector<std::string> tokens = SplitString(line, ' ');
1002  if (tokens.size() < 5) continue;
1003  if (tokens[4] != loader_exports_->mount_point) continue;
1004  unsigned i = 5;
1005  for (; i < tokens.size(); ++i) {
1006  if (tokens[i] == "-") break;
1007  }
1008  if (tokens.size() < i + 3) continue;
1009  if (tokens[i + 2] != "cvmfs2") continue;
1010  loader_exports_->device_id = tokens[2];
1011  break;
1012  }
1013  close(fd_mountinfo);
1014  }
1015 
1016  if (!premounted_) {
1017  LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: mounted cvmfs on %s",
1018  mount_point_->c_str());
1019  }
1021  "CernVM-FS: linking %s to repository %s",
1022  mount_point_->c_str(), repository_name_->c_str());
1023  if (!foreground_)
1024  Daemonize();
1025 
1028 
1029  SetLogMicroSyslog("");
1030  retval = fuse_set_signal_handlers(session);
1031  assert(retval == 0);
1032 #if CVMFS_USE_LIBFUSE == 2
1033  fuse_session_add_chan(session, channel);
1034 #endif
1035  if (single_threaded_) {
1036  retval = fuse_session_loop(session);
1037  } else {
1038 #if CVMFS_USE_LIBFUSE == 2
1039  retval = fuse_session_loop_mt(session);
1040 #else
1041  retval = fuse_session_loop_mt(session, 1 /* use fd per thread */);
1042 #endif
1043  }
1045 
1048 
1049  // Unmount
1050 #if CVMFS_USE_LIBFUSE == 2
1051  fuse_remove_signal_handlers(session);
1052  fuse_session_remove_chan(channel);
1053  fuse_session_destroy(session);
1054  fuse_unmount(mount_point_->c_str(), channel);
1055  channel = NULL;
1056 #else
1057  // libfuse3
1058  fuse_remove_signal_handlers(session);
1059  fuse_session_unmount(session);
1060  fuse_session_destroy(session);
1061 #endif
1062  fuse_opt_free_args(mount_options);
1063  delete mount_options;
1064  session = NULL;
1065  mount_options = NULL;
1066 
1067  CloseLibrary();
1068 
1069  LogCvmfs(kLogCvmfs, kLogSyslog, "CernVM-FS: unmounted %s (%s)",
1070  mount_point_->c_str(), repository_name_->c_str());
1071 
1072  delete fence_reload_;
1073  delete loader_exports_;
1074  delete config_files_;
1075  delete repository_name_;
1076  delete mount_point_;
1077  delete socket_path_;
1078  fence_reload_ = NULL;
1079  loader_exports_ = NULL;
1080  config_files_ = NULL;
1081  repository_name_ = NULL;
1082  mount_point_ = NULL;
1083  socket_path_ = NULL;
1084 
1085  if (retval != 0)
1086  return kFailFuseLoop;
1087  return kFailOk;
1088 }
1089 
1090 
1091 __attribute__((visibility("default")))
1092 CvmfsStubExports *g_cvmfs_stub_exports = NULL;
1093 
1094 static void __attribute__((constructor)) LibraryMain() {
1095  g_cvmfs_stub_exports = new CvmfsStubExports();
1096  g_cvmfs_stub_exports->fn_main = FuseMain;
1097 }
1098 
1099 static void __attribute__((destructor)) LibraryExit() {
1100  delete g_cvmfs_stub_exports;
1101  g_cvmfs_stub_exports = NULL;
1102 }
bool MakeCacheDirectories(const std::string &path, const mode_t mode)
Definition: posix.cc:869
bool simple_options_parsing_
Definition: loader.cc:141
std::string repository_name
Definition: loader.h:177
Failures Reload(const int fd_progress, const bool stop_and_go)
Definition: loader.cc:546
#define LogCvmfs(source, mask,...)
Definition: logging.h:22
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:178
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:137
virtual void ParsePath(const std::string &config_file, const bool external)=0
Session * session() const
Definition: repository.h:319
static struct fuse_opt cvmfs_array_opts[]
Definition: loader.cc:90
EventList history
Definition: loader.h:181
bool grab_mountpoint_
Definition: loader.cc:136
static CvmfsExports * LoadLibrary(const bool debug_mode, LoaderExports *loader_exports)
Definition: loader.cc:482
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:232
int FuseMain(int argc, char *argv[])
Definition: loader.cc:610
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
Definition: posix.cc:275
#define PANIC(...)
Definition: exception.h:27
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:138
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:235
void Daemonize()
Definition: posix.cc:1604
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:1890
void SendMsg2Socket(const int fd, const std::string &msg)
Definition: posix.cc:654
assert((mem||(size==0))&&"Out Of Memory")
void ** fuse_channel_or_session
Definition: loader.h:195
MountPoint * mount_point_
Definition: cvmfs.cc:123
std::string program_name
Definition: loader.h:180
bool disable_watchdog_
Definition: loader.cc:140
std::string loader_version
Definition: loader.h:175
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:238
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:1435
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:1457
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
bool(* fnRestoreState)(const int fd_progress, const StateList &saved_states)
Definition: loader.h:239
#define CVMFS_OPT(t, p, v)
Definition: loader.cc:88
static fuse_args * ParseCmdLine(int argc, char *argv[])
Definition: loader.cc:404
const loader::LoaderExports * loader_exports_
Definition: cvmfs.cc:146
std::string config_files
Definition: loader.h:179
struct fuse_lowlevel_ops cvmfs_operations
Definition: loader.h:242
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
Definition: posix.cc:833
void Drain()
Definition: fence.h:52
void WaitForSignal(int signum)
Definition: posix.cc:1565
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:441
void Usage()
int(* fnInit)(const LoaderExports *loader_exports)
Definition: loader.h:233
bool simple_options_parsing
Definition: loader.h:188
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:1973
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:237
void(* fnFreeSavedState)(const int fd_progress, const StateList &saved_states)
Definition: loader.h:240
string StringifyInt(const int64_t value)
Definition: string.cc:78
Failures
Definition: loader.h:27
std::string device_id
Definition: loader.h:200
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:800
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:234
bool GetLineFd(const int fd, std::string *line)
Definition: string.cc:404
static void CloseLibrary()
Definition: loader.cc:468
uint64_t String2Uint64(const string &value)
Definition: string.cc:228
std::string(* fnGetErrorMsg)()
Definition: loader.h:236
static void Fini()
Definition: cvmfs.cc:2182
static void Spawn()
Definition: cvmfs.cc:2097
bool SwitchCredentials(const uid_t uid, const gid_t gid, const bool temporarily)
Definition: posix.cc:748
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:182
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:96
static void stub_init(void *userdata, struct fuse_conn_info *conn)
Definition: loader.cc:201
static void size_t size
Definition: smalloc.h:47
bool single_threaded_
Definition: loader.cc:132
CvmfsExports * cvmfs_exports_
Definition: loader.cc:144
static void * OpenLibrary(const string &path)
Definition: loader.cc:463
void BlockSignal(int signum)
Definition: posix.cc:1550
void Open()
Definition: fence.h:59
std::string * repository_name_
Definition: loader.cc:125