GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/loader.cc
Date: 2025-07-27 02:42:09
Exec Total Coverage
Lines: 0 572 0.0%
Branches: 0 976 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * Implements stub callback functions for Fuse. Their purpose is to
5 * redirect calls to the cvmfs shared library and to block calls during the
6 * update of the library.
7 *
8 * The main executable and the cvmfs shared library _must not_ share any
9 * symbols.
10 */
11
12 #define ENOATTR ENODATA /**< instead of including attr/xattr.h */
13 #define _FILE_OFFSET_BITS 64
14
15
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;
61 int system_mount;
62 int grab_mountpoint;
63 int cvmfs_suid;
64 int disable_watchdog;
65 int simple_options_parsing;
66 int foreground;
67 int fuse_debug;
68
69 // Ignored options
70 int ign_netdev;
71 int ign_user;
72 int ign_nouser;
73 int ign_users;
74 int ign_auto;
75 int ign_noauto;
76 int ign_libfuse;
77 };
78
79 enum {
80 KEY_HELP,
81 KEY_VERSION,
82 KEY_FOREGROUND,
83 KEY_SINGLETHREAD,
84 KEY_FUSE_DEBUG,
85 KEY_CVMFS_DEBUG,
86 KEY_OPTIONS_PARSE,
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 int fuse3_max_threads_ = 0;
131 int fuse3_idle_threads_ = 0;
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;
143 bool simple_options_parsing_ = false;
144 void *library_handle_;
145 Fence *fence_reload_;
146 CvmfsExports *cvmfs_exports_;
147 LoaderExports *loader_exports_;
148
149
150 static void Usage(const string &exename) {
151 LogCvmfs(kLogCvmfs, kLogStdout,
152 "Usage: %s [-h] [-V] [-s] [-f] [-d] [-k] [-o mount_options] "
153 "<repository_name> <mount_point>\n\n"
154 "Mounts a CernVM-FS with the given repository at the given mountpoint.\n"
155 "Usually invoked via autofs or 'mount -t cvmfs <repository_name> <mount_point>'\n\n"
156 "CernVM-FS general options:\n"
157 " -h, --help Print Help output (this)\n"
158 " -V, --version 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"
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 exename.c_str());
180 }
181
182 /**
183 * For an premounted mountpoint, the argument is the file descriptor to
184 * /dev/fuse provided in the form /dev/fd/%d
185 */
186 bool CheckPremounted(const std::string &mountpoint) {
187 int len;
188 unsigned fd;
189 const bool retval = (sscanf(mountpoint.c_str(), "/dev/fd/%u%n", &fd, &len)
190 == 1)
191 && (len >= 0)
192 && (static_cast<unsigned>(len) == mountpoint.length());
193 if (retval) {
194 LogCvmfs(kLogCvmfs, kLogStdout,
195 "CernVM-FS: pre-mounted on file descriptor %d", fd);
196 return true;
197 }
198 return false;
199 }
200
201
202 static void stub_init(void *userdata, struct fuse_conn_info *conn) {
203 const FenceGuard fence_guard(fence_reload_);
204 cvmfs_exports_->cvmfs_operations.init(userdata, conn);
205 }
206
207
208 static void stub_destroy(void *userdata) {
209 const FenceGuard fence_guard(fence_reload_);
210 cvmfs_exports_->cvmfs_operations.destroy(userdata);
211 }
212
213
214 static void stub_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
215 const FenceGuard fence_guard(fence_reload_);
216 cvmfs_exports_->cvmfs_operations.lookup(req, parent, name);
217 }
218
219
220 static void stub_getattr(fuse_req_t req, fuse_ino_t ino,
221 struct fuse_file_info *fi) {
222 const FenceGuard fence_guard(fence_reload_);
223 cvmfs_exports_->cvmfs_operations.getattr(req, ino, fi);
224 }
225
226
227 static void stub_readlink(fuse_req_t req, fuse_ino_t ino) {
228 const FenceGuard fence_guard(fence_reload_);
229 cvmfs_exports_->cvmfs_operations.readlink(req, ino);
230 }
231
232
233 static void stub_opendir(fuse_req_t req, fuse_ino_t ino,
234 struct fuse_file_info *fi) {
235 const FenceGuard fence_guard(fence_reload_);
236 cvmfs_exports_->cvmfs_operations.opendir(req, ino, fi);
237 }
238
239
240 static void stub_releasedir(fuse_req_t req, fuse_ino_t ino,
241 struct fuse_file_info *fi) {
242 const FenceGuard fence_guard(fence_reload_);
243 cvmfs_exports_->cvmfs_operations.releasedir(req, ino, fi);
244 }
245
246
247 static void stub_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
248 struct fuse_file_info *fi) {
249 const FenceGuard fence_guard(fence_reload_);
250 cvmfs_exports_->cvmfs_operations.readdir(req, ino, size, off, fi);
251 }
252
253
254 static void stub_open(fuse_req_t req, fuse_ino_t ino,
255 struct fuse_file_info *fi) {
256 const FenceGuard fence_guard(fence_reload_);
257 cvmfs_exports_->cvmfs_operations.open(req, ino, fi);
258 }
259
260
261 static void stub_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
262 struct fuse_file_info *fi) {
263 const FenceGuard fence_guard(fence_reload_);
264 cvmfs_exports_->cvmfs_operations.read(req, ino, size, off, fi);
265 }
266
267
268 static void stub_release(fuse_req_t req, fuse_ino_t ino,
269 struct fuse_file_info *fi) {
270 const FenceGuard fence_guard(fence_reload_);
271 cvmfs_exports_->cvmfs_operations.release(req, ino, fi);
272 }
273
274
275 static void stub_statfs(fuse_req_t req, fuse_ino_t ino) {
276 const FenceGuard fence_guard(fence_reload_);
277 cvmfs_exports_->cvmfs_operations.statfs(req, ino);
278 }
279
280
281 #ifdef __APPLE__
282 static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
283 size_t size, uint32_t position)
284 #else
285 static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
286 size_t size)
287 #endif
288 {
289 const FenceGuard fence_guard(fence_reload_);
290 #ifdef __APPLE__
291 cvmfs_exports_->cvmfs_operations.getxattr(req, ino, name, size, position);
292 #else
293 cvmfs_exports_->cvmfs_operations.getxattr(req, ino, name, size);
294 #endif
295 }
296
297
298 static void stub_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
299 const FenceGuard fence_guard(fence_reload_);
300 cvmfs_exports_->cvmfs_operations.listxattr(req, ino, size);
301 }
302
303
304 static void stub_forget(fuse_req_t req,
305 fuse_ino_t ino,
306 #if CVMFS_USE_LIBFUSE == 2
307 unsigned long nlookup // NOLINT
308 #else
309 uint64_t nlookup
310 #endif
311 ) {
312 const FenceGuard fence_guard(fence_reload_);
313 cvmfs_exports_->cvmfs_operations.forget(req, ino, nlookup);
314 }
315
316
317 #if (FUSE_VERSION >= 29)
318 static void stub_forget_multi(fuse_req_t req,
319 size_t count,
320 struct fuse_forget_data *forgets) {
321 const FenceGuard fence_guard(fence_reload_);
322 cvmfs_exports_->cvmfs_operations.forget_multi(req, count, forgets);
323 }
324 #endif
325
326
327 /**
328 * The callback used when fuse is parsing all the options
329 * We separate CVMFS options from FUSE options here.
330 *
331 * \return On success zero, else non-zero
332 */
333 static int ParseFuseOptions(void *data __attribute__((unused)), const char *arg,
334 int key, struct fuse_args *outargs) {
335 unsigned arglen = 0;
336 if (arg)
337 arglen = strlen(arg);
338 switch (key) {
339 case FUSE_OPT_KEY_OPT:
340 // Check if it a cvmfs option
341 if ((arglen > 0) && (arg[0] != '-')) {
342 const char **o;
343 for (o = (const char **)cvmfs_array_opts; *o; o++) {
344 const unsigned olen = strlen(*o);
345 if ((arglen > olen && arg[olen] == '=')
346 && (strncasecmp(arg, *o, olen) == 0))
347 return 0;
348 }
349 }
350 return 1;
351
352 case FUSE_OPT_KEY_NONOPT:
353 // first: repository name, second: mount point
354 assert(arg != NULL);
355 if (!repository_name_) {
356 repository_name_ = new string(arg);
357 } else {
358 if (mount_point_)
359 return 1;
360 mount_point_ = new string(arg);
361 premounted_ = CheckPremounted(*mount_point_);
362 }
363 return 0;
364
365 case KEY_HELP:
366 Usage(outargs->argv[0]);
367 exit(0);
368 case KEY_VERSION:
369 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS version %s\n", CVMFS_VERSION);
370 exit(0);
371 case KEY_FOREGROUND:
372 foreground_ = true;
373 return 0;
374 case KEY_SINGLETHREAD:
375 single_threaded_ = true;
376 return 0;
377 case KEY_FUSE_DEBUG:
378 fuse_opt_add_arg(outargs, "-d");
379 case KEY_CVMFS_DEBUG:
380 debug_mode_ = true;
381 return 0;
382 case KEY_OPTIONS_PARSE:
383 parse_options_only_ = true;
384 return 0;
385 default:
386 PANIC(kLogStderr, "internal option parsing error");
387 }
388 }
389
390 static fuse_args *ParseCmdLine(int argc, char *argv[]) {
391 struct fuse_args *mount_options = new fuse_args();
392 CvmfsOptions cvmfs_options;
393 memset(&cvmfs_options, 0, sizeof(cvmfs_options));
394
395 mount_options->argc = argc;
396 mount_options->argv = argv;
397 mount_options->allocated = 0;
398 if ((fuse_opt_parse(mount_options, &cvmfs_options, cvmfs_array_opts,
399 ParseFuseOptions)
400 != 0)
401 || !mount_point_ || !repository_name_) {
402 delete mount_options;
403 return NULL;
404 }
405 if (cvmfs_options.config) {
406 config_files_ = new string(cvmfs_options.config);
407 free(cvmfs_options.config);
408 }
409 uid_ = cvmfs_options.uid;
410 gid_ = cvmfs_options.gid;
411 system_mount_ = cvmfs_options.system_mount;
412 grab_mountpoint_ = cvmfs_options.grab_mountpoint;
413 suid_mode_ = cvmfs_options.cvmfs_suid;
414 disable_watchdog_ = cvmfs_options.disable_watchdog;
415 simple_options_parsing_ = cvmfs_options.simple_options_parsing;
416 if (cvmfs_options.foreground) {
417 foreground_ = true;
418 }
419 if (cvmfs_options.fuse_debug) {
420 fuse_opt_add_arg(mount_options, "-d");
421 }
422
423 return mount_options;
424 }
425
426 #if CVMFS_USE_LIBFUSE != 2
427 static bool MatchFuseOption(const fuse_args *mount_options, const char *opt) {
428 for (int i = 0; i < mount_options->argc; i++) {
429 char *arg = mount_options->argv[i];
430 char *p = strstr(arg, opt);
431 if (p != NULL) {
432 if (p == arg)
433 return true;
434 const char c = *(p - 1);
435 if ((c == ',') || (c == ' '))
436 return true;
437 if ((c == 'o') && (p >= arg + 2) && (*(p - 2) == '-'))
438 return true;
439 }
440 }
441 return false;
442 }
443 #endif
444
445 static void SetFuseOperations(struct fuse_lowlevel_ops *loader_operations) {
446 memset(loader_operations, 0, sizeof(*loader_operations));
447
448 loader_operations->init = stub_init;
449 loader_operations->destroy = stub_destroy;
450
451 loader_operations->lookup = stub_lookup;
452 loader_operations->getattr = stub_getattr;
453 loader_operations->readlink = stub_readlink;
454 loader_operations->open = stub_open;
455 loader_operations->read = stub_read;
456 loader_operations->release = stub_release;
457 loader_operations->opendir = stub_opendir;
458 loader_operations->readdir = stub_readdir;
459 loader_operations->releasedir = stub_releasedir;
460 loader_operations->statfs = stub_statfs;
461 loader_operations->getxattr = stub_getxattr;
462 loader_operations->listxattr = stub_listxattr;
463 loader_operations->forget = stub_forget;
464 }
465
466
467 static void *OpenLibrary(const string &path) {
468 return dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
469 }
470
471
472 static void CloseLibrary() {
473 #ifdef HAS_VALGRIND_HEADERS
474 // If the libcvmfs_fuse library is unloaded, valgrind can't resolve the
475 // symbols anymore. We skip under valgrind.
476 if (!RUNNING_ON_VALGRIND) {
477 #endif
478 dlclose(library_handle_);
479 library_handle_ = NULL;
480 #ifdef HAS_VALGRIND_HEADERS
481 }
482 #endif
483 }
484
485
486 static CvmfsExports *LoadLibrary(const bool debug_mode,
487 LoaderExports *loader_exports) {
488 std::string local_lib_path = "./";
489 if (getenv("CVMFS_LIBRARY_PATH") != NULL) {
490 local_lib_path = getenv("CVMFS_LIBRARY_PATH");
491 if (!local_lib_path.empty() && (*local_lib_path.rbegin() != '/'))
492 local_lib_path.push_back('/');
493 }
494
495 #if CVMFS_USE_LIBFUSE == 2
496 string library_name = string("cvmfs_fuse") + ((debug_mode) ? "_debug" : "");
497 #else
498 string library_name = string("cvmfs_fuse3") + ((debug_mode) ? "_debug" : "");
499 #endif
500 library_name = platform_libname(library_name);
501 string error_messages;
502
503 vector<string> library_paths; // TODO(rmeusel): C++11 initializer
504 if (library_paths.empty()) {
505 library_paths.push_back(local_lib_path + library_name);
506 library_paths.push_back("/usr/lib/" + library_name);
507 library_paths.push_back("/usr/lib64/" + library_name);
508 #ifdef __APPLE__
509 // Since OS X El Capitan (10.11) came with SIP, we needed to relocate our
510 // binaries from /usr/... to /usr/local/...
511 library_paths.push_back("/usr/local/lib/" + library_name);
512 #endif
513 }
514
515 vector<string>::const_iterator i = library_paths.begin();
516 const vector<string>::const_iterator iend = library_paths.end();
517 for (; i != iend; ++i) { // TODO(rmeusel): C++11 range based for
518 library_handle_ = OpenLibrary(*i);
519 if (library_handle_ != NULL) {
520 break;
521 }
522
523 error_messages += string(dlerror()) + "\n";
524 }
525
526 if (!library_handle_) {
527 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
528 "failed to load cvmfs library, tried: '%s'\n%s",
529 JoinStrings(library_paths, "' '").c_str(), error_messages.c_str());
530 return NULL;
531 }
532
533 CvmfsExports **exports_ptr = reinterpret_cast<CvmfsExports **>(
534 dlsym(library_handle_, "g_cvmfs_exports"));
535 if (!exports_ptr)
536 return NULL;
537
538 if (loader_exports) {
539 LoadEvent *load_event = new LoadEvent();
540 load_event->timestamp = time(NULL);
541 load_event->so_version = (*exports_ptr)->so_version;
542 loader_exports->history.push_back(load_event);
543 }
544
545 return *exports_ptr;
546 }
547
548 Failures Reload(const int fd_progress, const bool stop_and_go,
549 const ReloadMode reload_mode) {
550 int retval;
551
552 // for legacy call we take the current state of debug_mode_
553 if (reload_mode == kReloadDebug) {
554 debug_mode_ = true;
555 } else if (reload_mode == kReloadNoDebug) {
556 debug_mode_ = false;
557 }
558
559 retval = cvmfs_exports_->fnMaintenanceMode(fd_progress);
560 if (!retval)
561 return kFailMaintenanceMode;
562
563 SendMsg2Socket(fd_progress, "Blocking new file system calls\n");
564 fence_reload_->Close();
565
566 SendMsg2Socket(fd_progress, "Waiting for active file system calls\n");
567 fence_reload_->Drain();
568
569 retval = cvmfs_exports_->fnSaveState(fd_progress,
570 &loader_exports_->saved_states);
571 if (!retval)
572 return kFailSaveState;
573
574 SendMsg2Socket(fd_progress, "Unloading Fuse module\n");
575 cvmfs_exports_->fnFini();
576 CloseLibrary();
577
578 if (stop_and_go) {
579 CreateFile(*socket_path_ + ".paused", 0600);
580 SendMsg2Socket(fd_progress, "Waiting for the delivery of SIGUSR1...\n");
581 WaitForSignal(SIGUSR1);
582 unlink((*socket_path_ + ".paused").c_str());
583 }
584
585 SendMsg2Socket(fd_progress, "Re-Loading Fuse module\n");
586 cvmfs_exports_ = LoadLibrary(debug_mode_, loader_exports_);
587 if (!cvmfs_exports_)
588 return kFailLoadLibrary;
589 retval = cvmfs_exports_->fnInit(loader_exports_);
590 if (retval != kFailOk) {
591 const string msg_progress = cvmfs_exports_->fnGetErrorMsg() + " ("
592 + StringifyInt(retval) + ")\n";
593 LogCvmfs(kLogCvmfs, kLogSyslogErr, "%s", msg_progress.c_str());
594 SendMsg2Socket(fd_progress, msg_progress);
595 return (Failures)retval;
596 }
597
598 retval = cvmfs_exports_->fnRestoreState(fd_progress,
599 loader_exports_->saved_states);
600 if (!retval)
601 return kFailRestoreState;
602 cvmfs_exports_->fnFreeSavedState(fd_progress, loader_exports_->saved_states);
603 for (unsigned i = 0, l = loader_exports_->saved_states.size(); i < l; ++i) {
604 delete loader_exports_->saved_states[i];
605 }
606 loader_exports_->saved_states.clear();
607
608 SendMsg2Socket(fd_progress, "Activating Fuse module\n");
609 cvmfs_exports_->fnSpawn();
610
611 fence_reload_->Open();
612 return kFailOk;
613 }
614
615 } // namespace loader
616
617
618 using namespace loader; // NOLINT(build/namespaces)
619
620 int FuseMain(int argc, char *argv[]) {
621 // Set a decent umask for new files (no write access to group/everyone).
622 // We want to allow group write access for the talk-socket.
623 umask(007);
624 // SIGUSR1 is used for the stop_and_go mode during reload
625 BlockSignal(SIGUSR1);
626
627 int retval;
628
629 // Jump into alternative process flavors (e.g. shared cache manager)
630 // We are here due to a fork+execve (ManagedExec in util.cc) or due to
631 // utility calls of cvmfs2
632 if ((argc > 1) && (strstr(argv[1], "__") == argv[1])) {
633 if (string(argv[1]) == string("__RELOAD__")) {
634 if (argc < 3)
635 return 1;
636 bool stop_and_go = false;
637 if ((argc > 3) && (string(argv[3]) == "stop_and_go"))
638 stop_and_go = true;
639
640 // always last param of the cvmfs2 __RELOAD__ command
641 // check if debug mode is requested
642 // NOTE:
643 // debug mode is decided based on CVMFS_DEBUGLOG being set or not
644 // this means: reloading is now always based on CVMFS_DEBUGLOG, and
645 // reload ignores the current state
646 //
647 // if you mount with debug but do not set CVMFS_DEBUGLOG and reload,
648 // you will reload with
649 if (std::string(argv[argc - 1]) == std::string("--debug")) {
650 debug_mode_ = true;
651 } else {
652 debug_mode_ = false;
653 }
654 retval = loader_talk::MainReload(argv[2], stop_and_go, debug_mode_);
655
656 if ((retval != 0) && (stop_and_go)) {
657 CreateFile(string(argv[2]) + ".paused.crashed", 0600);
658 }
659 return retval;
660 }
661
662 if (string(argv[1]) == string("__MK_ALIEN_CACHE__")) {
663 if (argc < 5)
664 return 1;
665 const string alien_cache_dir = argv[2];
666 const sanitizer::PositiveIntegerSanitizer sanitizer;
667 if (!sanitizer.IsValid(argv[3]) || !sanitizer.IsValid(argv[4]))
668 return 1;
669 const uid_t uid_owner = String2Uint64(argv[3]);
670 const gid_t gid_owner = String2Uint64(argv[4]);
671
672 int retval = MkdirDeep(alien_cache_dir, 0770);
673 if (!retval) {
674 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create %s",
675 alien_cache_dir.c_str());
676 return 1;
677 }
678 retval = chown(alien_cache_dir.c_str(), uid_owner, gid_owner);
679 if (retval != 0) {
680 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to set owner of %s to %d:%d",
681 alien_cache_dir.c_str(), uid_owner, gid_owner);
682 return 1;
683 }
684 retval = SwitchCredentials(uid_owner, gid_owner, false);
685 if (!retval) {
686 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to impersonate %d:%d",
687 uid_owner, gid_owner);
688 return 1;
689 }
690 // Allow access to user and group
691 retval = MakeCacheDirectories(alien_cache_dir, 0770);
692 if (!retval) {
693 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to create cache skeleton");
694 return 1;
695 }
696 return 0;
697 }
698
699 debug_mode_ = getenv("__CVMFS_DEBUG_MODE__") != NULL;
700 cvmfs_exports_ = LoadLibrary(debug_mode_, NULL);
701 if (!cvmfs_exports_)
702 return kFailLoadLibrary;
703 return cvmfs_exports_->fnAltProcessFlavor(argc, argv);
704 }
705
706 // Option parsing
707 struct fuse_args *mount_options;
708 mount_options = ParseCmdLine(argc, argv);
709 if (!mount_options) {
710 Usage(argv[0]);
711 return kFailOptions;
712 }
713
714 string parameter;
715 OptionsManager *options_manager;
716 if (simple_options_parsing_) {
717 options_manager = new SimpleOptionsParser(
718 new DefaultOptionsTemplateManager(*repository_name_));
719 } else {
720 options_manager = new BashOptionsManager(
721 new DefaultOptionsTemplateManager(*repository_name_));
722 }
723 if (config_files_) {
724 vector<string> tokens = SplitString(*config_files_, ':');
725 for (unsigned i = 0, s = tokens.size(); i < s; ++i) {
726 options_manager->ParsePath(tokens[i], false);
727 }
728 } else {
729 options_manager->ParseDefault(*repository_name_);
730 }
731
732 #ifdef __APPLE__
733 string volname = "-ovolname=" + *repository_name_;
734 fuse_opt_add_arg(mount_options, volname.c_str());
735 // Allow for up to 5 minute "hangs" before OS X may kill cvmfs
736 fuse_opt_add_arg(mount_options, "-odaemon_timeout=300");
737 fuse_opt_add_arg(mount_options, "-onoapplexattr");
738 // Should libfuse be single-threaded? See CVM-871, CVM-855
739 // single_threaded_ = true;
740 #endif
741 if (options_manager->GetValue("CVMFS_MOUNT_RW", &parameter)
742 && options_manager->IsOn(parameter)) {
743 fuse_opt_add_arg(mount_options, "-orw");
744 } else {
745 fuse_opt_add_arg(mount_options, "-oro");
746 }
747 fuse_opt_add_arg(mount_options, "-onodev");
748 if (options_manager->GetValue("CVMFS_SUID", &parameter)
749 && options_manager->IsOn(parameter)) {
750 suid_mode_ = true;
751 }
752 if (suid_mode_) {
753 if (getuid() != 0) {
754 PANIC(kLogStderr | kLogSyslogErr,
755 "must be root to mount with suid option");
756 }
757 fuse_opt_add_arg(mount_options, "-osuid");
758 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with suid support");
759 }
760
761 if (options_manager->GetValue("CVMFS_CPU_AFFINITY", &parameter)) {
762 #ifndef __APPLE__
763 cpu_set_t mask;
764 vector<string> cpus = SplitString(parameter, ',');
765 CPU_ZERO(&mask);
766 for (vector<string>::iterator i = cpus.begin(); i != cpus.end(); i++) {
767 CPU_SET(String2Uint64(Trim(*i)), &mask);
768 }
769 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: setting CPU Affinity to %s",
770 parameter.c_str());
771 const int err = sched_setaffinity(0, sizeof(mask), &mask);
772 if (err != 0) {
773 LogCvmfs(kLogCvmfs, kLogStdout | kLogSyslogErr,
774 "Setting CPU Affinity failed with error %d", errno);
775 }
776 #else
777 LogCvmfs(kLogCvmfs, kLogStdout | kLogSyslogErr,
778 "CPU affinity setting not supported on macOS");
779 #endif
780 }
781 loader_exports_ = new LoaderExports();
782 loader_exports_->loader_version = CVMFS_VERSION;
783 loader_exports_->boot_time = time(NULL);
784 loader_exports_->program_name = argv[0];
785 loader_exports_->foreground = foreground_;
786 loader_exports_->repository_name = *repository_name_;
787 loader_exports_->mount_point = *mount_point_;
788 loader_exports_->device_id = "0:0"; // initially unknown, set after mount
789 loader_exports_->disable_watchdog = disable_watchdog_;
790 loader_exports_->simple_options_parsing = simple_options_parsing_;
791 if (config_files_)
792 loader_exports_->config_files = *config_files_;
793 else
794 loader_exports_->config_files = "";
795
796 if (parse_options_only_) {
797 LogCvmfs(kLogCvmfs, kLogStdout, "# CernVM-FS parameters:\n%s",
798 options_manager->Dump().c_str());
799 return 0;
800 }
801
802 // Logging
803 if (options_manager->GetValue("CVMFS_SYSLOG_LEVEL", &parameter))
804 SetLogSyslogLevel(String2Uint64(parameter));
805 else
806 SetLogSyslogLevel(3);
807 if (options_manager->GetValue("CVMFS_SYSLOG_FACILITY", &parameter))
808 SetLogSyslogFacility(String2Int64(parameter));
809 SetLogSyslogPrefix(*repository_name_);
810 // Deferr setting usyslog until credentials are dropped
811
812 // Permissions check
813 if (options_manager->GetValue("CVMFS_CHECK_PERMISSIONS", &parameter)) {
814 if (options_manager->IsOn(parameter)) {
815 fuse_opt_add_arg(mount_options, "-odefault_permissions");
816 }
817 }
818
819 if (!premounted_ && !DirectoryExists(*mount_point_)) {
820 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
821 "Mount point %s does not exist", mount_point_->c_str());
822 return kFailPermission;
823 }
824
825 // Number of file descriptors
826 if (options_manager->GetValue("CVMFS_NFILES", &parameter)) {
827 const int retval = SetLimitNoFile(String2Uint64(parameter));
828 if (retval == -2) {
829 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running under valgrind");
830 } else if (retval == -1) {
831 if (system_mount_) {
832 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
833 "Failed to set maximum number of open files, "
834 "insufficient permissions");
835 return kFailPermission;
836 }
837 unsigned soft_limit, hard_limit;
838 GetLimitNoFile(&soft_limit, &hard_limit);
839 LogCvmfs(kLogCvmfs, kLogStdout | kLogSyslogWarn,
840 "Failed to set requested number of open files, "
841 "using maximum number %u",
842 hard_limit);
843 if (hard_limit > soft_limit) {
844 (void)SetLimitNoFile(hard_limit);
845 }
846 }
847 }
848
849 // Apply OOM score adjustment
850 if (options_manager->GetValue("CVMFS_OOM_SCORE_ADJ", &parameter)) {
851 const string proc_path = "/proc/" + StringifyInt(getpid())
852 + "/oom_score_adj";
853 const int fd_oom = open(proc_path.c_str(), O_WRONLY);
854 if (fd_oom < 0) {
855 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn, "failed to open %s",
856 proc_path.c_str());
857 } else {
858 const bool retval = SafeWrite(fd_oom, parameter.data(),
859 parameter.length());
860 if (!retval) {
861 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn,
862 "failed to set OOM score adjustment to %s", parameter.c_str());
863 }
864 close(fd_oom);
865 }
866 }
867
868 // Protect the process from being killed by systemd
869 if (options_manager->GetValue("CVMFS_SYSTEMD_NOKILL", &parameter)
870 && options_manager->IsOn(parameter)) {
871 argv[0][0] = '@';
872 }
873
874 // Grab mountpoint
875 if (grab_mountpoint_) {
876 if ((chown(mount_point_->c_str(), uid_, gid_) != 0)
877 || (chmod(mount_point_->c_str(), 0755) != 0)) {
878 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
879 "Failed to grab mountpoint %s (%d)", mount_point_->c_str(),
880 errno);
881 return kFailPermission;
882 }
883 }
884
885 #if CVMFS_USE_LIBFUSE != 2
886 int premount_fd = -1;
887 if (!premounted_ && !suid_mode_ && getuid() == 0) {
888 // If not already premounted or using suid mode, premount the fuse
889 // mountpoint before dropping privileges to avoid the need for fusermount.
890 // Requires libfuse >= 3.3.0.
891 platform_stat64 info;
892 // Need to know if it is a directory or not
893 if (platform_stat(mount_point_->c_str(), &info) != 0) {
894 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
895 "Failed to stat mountpoint %s (%d)", mount_point_->c_str(),
896 errno);
897 return kFailPermission;
898 }
899 premount_fd = open("/dev/fuse", O_RDWR);
900 if (premount_fd == -1) {
901 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
902 "Failed to open /dev/fuse (%d)", errno);
903 return kFailPermission;
904 }
905 char opts[128];
906 snprintf(
907 opts, sizeof(opts), "fd=%i,rootmode=%o,user_id=0,group_id=0%s%s",
908 premount_fd, info.st_mode & S_IFMT,
909 MatchFuseOption(mount_options, "default_permissions")
910 ? ",default_permissions"
911 : "",
912 MatchFuseOption(mount_options, "allow_other") ? ",allow_other" : "");
913 unsigned long flags = MS_NOSUID | MS_NODEV | MS_RELATIME;
914 // Note that during the handling of the `CVMFS_MOUNT_RW` option, we ensure
915 // that at least one of `rw` or `ro` is part of the mount option string (we
916 // won't have both unset). If both `rw` and `ro` are set, the read-only
917 // option takes precedence.
918 if (MatchFuseOption(mount_options, "ro")) {
919 flags |= MS_RDONLY;
920 }
921 if (mount("cvmfs2", mount_point_->c_str(), "fuse", flags, opts) == -1) {
922 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
923 "Failed to mount -t fuse -o %s cvmfs2 %s (%d)", opts,
924 mount_point_->c_str(), errno);
925 return kFailPermission;
926 }
927 }
928 #endif
929
930 int fd_mountinfo = -1; // needs to be declared before start using goto
931
932 // Drop credentials
933 if ((uid_ != 0) || (gid_ != 0)) {
934 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: running with credentials %d:%d",
935 uid_, gid_);
936 const bool retrievable = (suid_mode_ || !disable_watchdog_);
937 if (!SwitchCredentials(uid_, gid_, retrievable)) {
938 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
939 "Failed to drop credentials");
940 retval = kFailPermission;
941 goto cleanup;
942 }
943 }
944 if (disable_watchdog_) {
945 LogCvmfs(kLogCvmfs, kLogDebug, "No watchdog, enabling core files");
946 prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
947 }
948
949 // Only set usyslog now, otherwise file permissions are wrong
950 usyslog_path_ = new string();
951 if (options_manager->GetValue("CVMFS_USYSLOG", &parameter))
952 *usyslog_path_ = parameter;
953 SetLogMicroSyslog(*usyslog_path_);
954
955 if (single_threaded_) {
956 LogCvmfs(kLogCvmfs, kLogStdout,
957 "CernVM-FS: running in single threaded mode");
958 }
959 if (debug_mode_) {
960 LogCvmfs(kLogCvmfs, kLogStdout | kLogSyslogWarn,
961 "CernVM-FS: running in debug mode");
962 }
963
964 #ifndef FUSE_CAP_POSIX_ACL
965 if (options_manager->GetValue("CVMFS_ENFORCE_ACLS", &parameter)
966 && options_manager->IsOn(parameter)) {
967 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
968 "CernVM-FS: ACL support requested but not available in this "
969 "version of libfuse");
970 retval = kFailPermission;
971 goto cleanup;
972 }
973 #endif
974
975 // Initialize the loader socket, connections are not accepted until Spawn()
976 socket_path_ = new string("/var/run/cvmfs");
977 if (options_manager->GetValue("CVMFS_RELOAD_SOCKETS", &parameter))
978 *socket_path_ = MakeCanonicalPath(parameter);
979 *socket_path_ += "/cvmfs." + *repository_name_;
980 retval = loader_talk::Init(*socket_path_);
981 if (!retval) {
982 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
983 "Failed to initialize loader socket");
984 retval = kFailLoaderTalk;
985 goto cleanup;
986 }
987
988 // TODO(jblomer): we probably want to apply a default setting related to the
989 // number of cores.
990 if (options_manager->GetValue("CVMFS_FUSE3_MAX_THREADS", &parameter)) {
991 fuse3_max_threads_ = String2Int64(parameter);
992 }
993 if (options_manager->GetValue("CVMFS_FUSE3_IDLE_THREADS", &parameter)) {
994 fuse3_idle_threads_ = String2Int64(parameter);
995 }
996 #ifdef CVMFS_ENABLE_FUSE3_LOOP_CONFIG
997 if (fuse3_max_threads_) {
998 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: Fuse3 max_threads=%d",
999 fuse3_max_threads_);
1000 }
1001 if (fuse3_idle_threads_) {
1002 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: Fuse3 min_idle_threads=%d",
1003 fuse3_idle_threads_);
1004 }
1005 #else
1006 if (fuse3_max_threads_ || fuse3_idle_threads_) {
1007 LogCvmfs(kLogCvmfs, kLogStdout,
1008 "CernVM-FS: ignoring fuse3 thread settings (libfuse too old)");
1009 }
1010 #endif
1011
1012 // Options are not needed anymore
1013 delete options_manager;
1014 options_manager = NULL;
1015
1016 struct fuse_session *session;
1017 #if CVMFS_USE_LIBFUSE == 2
1018 struct fuse_chan *channel;
1019 loader_exports_->fuse_channel_or_session = reinterpret_cast<void **>(
1020 &channel);
1021 #else
1022 loader_exports_->fuse_channel_or_session = reinterpret_cast<void **>(
1023 &session);
1024 #endif
1025
1026 // Load and initialize cvmfs library
1027 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak,
1028 "CernVM-FS: loading Fuse module... ");
1029 cvmfs_exports_ = LoadLibrary(debug_mode_, loader_exports_);
1030 if (!cvmfs_exports_) {
1031 retval = kFailLoadLibrary;
1032 goto cleanup;
1033 }
1034 retval = cvmfs_exports_->fnInit(loader_exports_);
1035 if (retval != kFailOk) {
1036 if (retval == kFailDoubleMount) {
1037 LogCvmfs(kLogCvmfs, kLogStderr,
1038 "\nCernVM-FS: repository %s already mounted on %s",
1039 loader_exports_->repository_name.c_str(),
1040 loader_exports_->mount_point.c_str());
1041 return 0;
1042 }
1043 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr, "%s (%d - %s)",
1044 cvmfs_exports_->fnGetErrorMsg().c_str(), retval,
1045 Code2Ascii((Failures)retval));
1046 cvmfs_exports_->fnFini();
1047 goto cleanup;
1048 }
1049 LogCvmfs(kLogCvmfs, kLogStdout, "done");
1050
1051 // Mount
1052 fence_reload_ = new Fence();
1053
1054 if (suid_mode_) {
1055 const bool retrievable = true;
1056 if (!SwitchCredentials(0, getgid(), retrievable)) {
1057 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1058 "failed to re-gain root permissions for mounting");
1059 cvmfs_exports_->fnFini();
1060 retval = kFailPermission;
1061 goto cleanup;
1062 }
1063 }
1064
1065
1066 struct fuse_lowlevel_ops loader_operations;
1067 SetFuseOperations(&loader_operations);
1068 #if (FUSE_VERSION >= 29)
1069 if (cvmfs_exports_->cvmfs_operations.forget_multi)
1070 loader_operations.forget_multi = stub_forget_multi;
1071 #endif
1072
1073 #if CVMFS_USE_LIBFUSE == 2
1074 channel = fuse_mount(mount_point_->c_str(), mount_options);
1075 if (!channel) {
1076 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1077 "failed to create Fuse channel");
1078 cvmfs_exports_->fnFini();
1079 retval = kFailMount;
1080 goto cleanup;
1081 }
1082
1083 session = fuse_lowlevel_new(mount_options, &loader_operations,
1084 sizeof(loader_operations), NULL);
1085 if (!session) {
1086 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1087 "failed to create Fuse session");
1088 fuse_unmount(mount_point_->c_str(), channel);
1089 cvmfs_exports_->fnFini();
1090 retval = kFailMount;
1091 goto cleanup;
1092 }
1093 #else
1094 // libfuse3
1095 session = fuse_session_new(mount_options, &loader_operations,
1096 sizeof(loader_operations), NULL);
1097 if (!session) {
1098 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1099 "failed to create Fuse session");
1100 cvmfs_exports_->fnFini();
1101 retval = kFailMount;
1102 goto cleanup;
1103 }
1104 if (premount_fd >= 0) {
1105 char premount_str[64];
1106 snprintf(premount_str, sizeof(premount_str), "/dev/fd/%d", premount_fd);
1107 retval = fuse_session_mount(session, premount_str);
1108 } else {
1109 retval = fuse_session_mount(session, mount_point_->c_str());
1110 }
1111 if (retval != 0) {
1112 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1113 "failed to mount file system");
1114 cvmfs_exports_->fnFini();
1115 retval = kFailMount;
1116 goto cleanup;
1117 }
1118 #endif
1119
1120 // drop credentials
1121 if (suid_mode_) {
1122 const bool retrievable = !disable_watchdog_;
1123 if (!SwitchCredentials(uid_, gid_, retrievable)) {
1124 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1125 "failed to drop permissions after mounting");
1126 cvmfs_exports_->fnFini();
1127 retval = kFailPermission;
1128 goto cleanup;
1129 }
1130 }
1131
1132 // Determine device id
1133 fd_mountinfo = open("/proc/self/mountinfo", O_RDONLY);
1134 if (fd_mountinfo > 0) {
1135 std::string line;
1136 while (GetLineFd(fd_mountinfo, &line)) {
1137 std::vector<std::string> tokens = SplitString(line, ' ');
1138 if (tokens.size() < 5)
1139 continue;
1140 if (tokens[4] != loader_exports_->mount_point)
1141 continue;
1142 unsigned i = 5;
1143 for (; i < tokens.size(); ++i) {
1144 if (tokens[i] == "-")
1145 break;
1146 }
1147 if (tokens.size() < i + 3)
1148 continue;
1149 if (tokens[i + 2] != "cvmfs2")
1150 continue;
1151 loader_exports_->device_id = tokens[2];
1152 break;
1153 }
1154 close(fd_mountinfo);
1155 }
1156
1157 if (!premounted_) {
1158 LogCvmfs(kLogCvmfs, kLogStdout, "CernVM-FS: mounted cvmfs on %s",
1159 mount_point_->c_str());
1160 }
1161 LogCvmfs(kLogCvmfs, kLogSyslog, "CernVM-FS: linking %s to repository %s",
1162 mount_point_->c_str(), repository_name_->c_str());
1163 if (!foreground_)
1164 Daemonize();
1165
1166 cvmfs_exports_->fnSpawn();
1167 loader_talk::Spawn();
1168
1169 SetLogMicroSyslog("");
1170 retval = fuse_set_signal_handlers(session);
1171 assert(retval == 0);
1172 #if CVMFS_USE_LIBFUSE == 2
1173 fuse_session_add_chan(session, channel);
1174 #endif
1175 if (single_threaded_) {
1176 retval = fuse_session_loop(session);
1177 } else {
1178 #if CVMFS_USE_LIBFUSE == 2
1179 retval = fuse_session_loop_mt(session);
1180 #else
1181 #ifdef CVMFS_ENABLE_FUSE3_LOOP_CONFIG
1182 struct fuse_loop_config *fuse_loop_cfg = fuse_loop_cfg_create();
1183
1184 fuse_loop_cfg_set_clone_fd(fuse_loop_cfg, 1);
1185
1186 if (fuse3_max_threads_ > 0) {
1187 fuse_loop_cfg_set_max_threads(fuse_loop_cfg, fuse3_max_threads_);
1188 }
1189 if (fuse3_idle_threads_ > 0) {
1190 fuse_loop_cfg_set_idle_threads(fuse_loop_cfg, fuse3_idle_threads_);
1191 }
1192
1193 retval = fuse_session_loop_mt(session, fuse_loop_cfg);
1194 fuse_loop_cfg_destroy(fuse_loop_cfg);
1195 #else
1196 retval = fuse_session_loop_mt(session, 1 /* use fd per thread */);
1197 #endif // CVMFS_ENABLE_FUSE3_LOOP_CONFIG
1198 #endif // fuse2/3
1199 }
1200 SetLogMicroSyslog(*usyslog_path_);
1201
1202 loader_talk::Fini();
1203 cvmfs_exports_->fnFini();
1204
1205 // Unmount
1206 #if CVMFS_USE_LIBFUSE == 2
1207 fuse_remove_signal_handlers(session);
1208 fuse_session_remove_chan(channel);
1209 fuse_session_destroy(session);
1210 fuse_unmount(mount_point_->c_str(), channel);
1211 channel = NULL;
1212 #else
1213 // libfuse3
1214 fuse_remove_signal_handlers(session);
1215 fuse_session_unmount(session);
1216 fuse_session_destroy(session);
1217 #endif
1218 fuse_opt_free_args(mount_options);
1219 delete mount_options;
1220 session = NULL;
1221 mount_options = NULL;
1222
1223 CloseLibrary();
1224
1225 delete fence_reload_;
1226 delete loader_exports_;
1227 delete config_files_;
1228 delete socket_path_;
1229 fence_reload_ = NULL;
1230 loader_exports_ = NULL;
1231 config_files_ = NULL;
1232 socket_path_ = NULL;
1233
1234 if (retval != 0)
1235 retval = kFailFuseLoop;
1236 else
1237 retval = kFailOk;
1238
1239 #if CVMFS_USE_LIBFUSE != 2
1240 if (premount_fd >= 0) {
1241 goto cleanup;
1242 }
1243 #endif
1244
1245 LogCvmfs(kLogCvmfs, kLogSyslog, "CernVM-FS: unmounted %s (%s)",
1246 mount_point_->c_str(), repository_name_->c_str());
1247
1248 delete repository_name_;
1249 delete mount_point_;
1250 repository_name_ = NULL;
1251 mount_point_ = NULL;
1252
1253 return retval;
1254
1255 cleanup:
1256 #if CVMFS_USE_LIBFUSE != 2
1257 if (premount_fd >= 0) {
1258 if (!SwitchCredentials(0, getgid(), true)) {
1259 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1260 "failed to re-gain root permissions for umounting");
1261 retval = kFailPermission;
1262 // do lazy unmount and ignore if it is already unmounted
1263 } else if (umount2(mount_point_->c_str(), MNT_DETACH) < 0 && errno != EINVAL) {
1264 LogCvmfs(kLogCvmfs, kLogStderr | kLogSyslogErr,
1265 "failed to umount %s (%d)", mount_point_->c_str(), errno);
1266 } else {
1267 LogCvmfs(kLogCvmfs, kLogSyslog, "CernVM-FS: unmounted %s (%s)",
1268 mount_point_->c_str(), repository_name_->c_str());
1269 }
1270 close(premount_fd);
1271 }
1272 #endif
1273
1274 delete repository_name_;
1275 delete mount_point_;
1276 repository_name_ = NULL;
1277 mount_point_ = NULL;
1278
1279 return retval;
1280 }
1281
1282
1283 __attribute__((visibility("default")))
1284 CvmfsStubExports *g_cvmfs_stub_exports = NULL;
1285
1286 static void __attribute__((constructor)) LibraryMain() {
1287 g_cvmfs_stub_exports = new CvmfsStubExports();
1288 g_cvmfs_stub_exports->fn_main = FuseMain;
1289 }
1290
1291 static void __attribute__((destructor)) LibraryExit() {
1292 delete g_cvmfs_stub_exports;
1293 g_cvmfs_stub_exports = NULL;
1294 }
1295