GCC Code Coverage Report


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