GCC Code Coverage Report


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