GCC Code Coverage Report


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