GCC Code Coverage Report


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