12 #define ENOATTR ENODATA
13 #define _FILE_OFFSET_BITS 64
24 #include <sys/resource.h>
29 #ifdef HAS_VALGRIND_HEADERS
30 #include <valgrind/valgrind.h>
88 #define CVMFS_OPT(t, p, v) \
89 { t, offsetof(struct CvmfsOptions, p), v }
90 #define CVMFS_SWITCH(t, p) \
91 { t, offsetof(struct CvmfsOptions, p), 1 }
100 CVMFS_SWITCH(
"simple_options_parsing", simple_options_parsing),
152 static void Usage(
const string &exename) {
155 "The CernVM File System\n"
157 "Copyright (c) 2009- CERN, all rights reserved\n\n"
158 "Please visit http://cernvm.cern.ch for details.\n\n"
159 "Usage: %s [-h] [-V] [-s] [-f] [-d] [-k] [-o mount options] "
160 "<repository name> <mount point>\n\n"
161 "CernVM-FS general options:\n"
162 " --help|-h Print Help output (this)\n"
163 " --version|-V 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\n"
177 " -o disable_watchdog Do not spawn a post mortem crash handler\n"
178 " -o foreground Run in foreground\n"
179 " -o libfuse=[2,3] Enforce a certain libfuse version\n"
180 "Fuse mount options:\n"
181 " -o allow_other allow access to other users\n"
182 " -o allow_root allow access to root\n"
183 " -o nonempty allow mounts over non-empty directory\n",
184 CVMFS_VERSION, exename.c_str());
195 (sscanf(mountpoint.c_str(),
"/dev/fd/%u%n", &fd, &len) == 1) &&
196 (len >= 0) && (static_cast<unsigned>(len) == mountpoint.length());
199 "CernVM-FS: pre-mounted on file descriptor %d", fd);
206 static void stub_init(
void *userdata,
struct fuse_conn_info *conn) {
218 static void stub_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name) {
225 struct fuse_file_info *fi) {
238 struct fuse_file_info *fi) {
245 struct fuse_file_info *fi) {
252 struct fuse_file_info *fi) {
259 struct fuse_file_info *fi) {
265 static void stub_read(fuse_req_t req, fuse_ino_t ino,
size_t size, off_t off,
266 struct fuse_file_info *fi) {
273 struct fuse_file_info *fi) {
286 static void stub_getxattr(fuse_req_t req, fuse_ino_t ino,
const char *name,
287 size_t size, uint32_t position)
310 #
if CVMFS_USE_LIBFUSE == 2
311 unsigned long nlookup
321 #if (FUSE_VERSION >= 29)
322 static void stub_forget_multi(fuse_req_t req,
324 struct fuse_forget_data *forgets) {
338 int key,
struct fuse_args *outargs) {
341 arglen = strlen(arg);
343 case FUSE_OPT_KEY_OPT:
345 if ((arglen > 0) && (arg[0] !=
'-')) {
348 const unsigned olen = strlen(*o);
349 if ((arglen > olen && arg[olen] ==
'=')
350 && (strncasecmp(arg, *o, olen) == 0))
356 case FUSE_OPT_KEY_NONOPT:
370 Usage(outargs->argv[0]);
382 fuse_opt_add_arg(outargs,
"-d");
395 struct fuse_args *mount_options =
new fuse_args();
397 memset(&cvmfs_options, 0,
sizeof(cvmfs_options));
399 mount_options->argc = argc;
400 mount_options->argv = argv;
401 mount_options->allocated = 0;
406 delete mount_options;
409 if (cvmfs_options.
config) {
411 free(cvmfs_options.
config);
424 fuse_opt_add_arg(mount_options,
"-d");
427 return mount_options;
432 for (
int i = 0; i < mount_options->argc; i++) {
433 char *arg = mount_options->argv[i];
434 char *p = strstr(arg, opt);
438 const char c = *(p - 1);
439 if ((c ==
',') || (c ==
' '))
441 if ((c ==
'o') && (p >= arg + 2) && (*(p - 2) ==
'-'))
450 memset(loader_operations, 0,
sizeof(*loader_operations));
472 return dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
477 #ifdef HAS_VALGRIND_HEADERS
480 if (!RUNNING_ON_VALGRIND) {
484 #ifdef HAS_VALGRIND_HEADERS
492 std::string local_lib_path =
"./";
493 if (getenv(
"CVMFS_LIBRARY_PATH") != NULL) {
494 local_lib_path = getenv(
"CVMFS_LIBRARY_PATH");
495 if (!local_lib_path.empty() && (*local_lib_path.rbegin() !=
'/'))
496 local_lib_path.push_back(
'/');
499 #if CVMFS_USE_LIBFUSE == 2
500 string library_name = string(
"cvmfs_fuse") + ((debug_mode) ?
"_debug" :
"");
502 string library_name = string(
"cvmfs_fuse3") + ((debug_mode) ?
"_debug" :
"");
505 string error_messages;
507 vector<string> library_paths;
508 if (library_paths.empty()) {
509 library_paths.push_back(local_lib_path + library_name);
510 library_paths.push_back(
"/usr/lib/" + library_name);
511 library_paths.push_back(
"/usr/lib64/" + library_name);
515 library_paths.push_back(
"/usr/local/lib/" + library_name);
519 vector<string>::const_iterator i = library_paths.begin();
520 const vector<string>::const_iterator iend = library_paths.end();
521 for (; i != iend; ++i) {
527 error_messages += string(dlerror()) +
"\n";
532 "failed to load cvmfs library, tried: '%s'\n%s",
533 JoinStrings(library_paths,
"' '").c_str(), error_messages.c_str());
542 if (loader_exports) {
545 load_event->
so_version = (*exports_ptr)->so_version;
546 loader_exports->
history.push_back(load_event);
570 SendMsg2Socket(fd_progress,
"Waiting for active file system calls\n");
584 SendMsg2Socket(fd_progress,
"Waiting for the delivery of SIGUSR1...\n");
595 const string msg_progress =
622 using namespace loader;
636 if ((argc > 1) && (strstr(argv[1],
"__") == argv[1])) {
637 if (
string(argv[1]) ==
string(
"__RELOAD__")) {
640 bool stop_and_go =
false;
641 if ((argc > 3) && (
string(argv[3]) ==
"stop_and_go"))
653 if (std::string(argv[argc - 1]) == std::string(
"--debug")) {
660 if ((retval != 0) && (stop_and_go)) {
661 CreateFile(
string(argv[2]) +
".paused.crashed", 0600);
666 if (
string(argv[1]) ==
string(
"__MK_ALIEN_CACHE__")) {
669 const string alien_cache_dir = argv[2];
676 int retval =
MkdirDeep(alien_cache_dir, 0770);
679 alien_cache_dir.c_str());
682 retval = chown(alien_cache_dir.c_str(), uid_owner, gid_owner);
685 alien_cache_dir.c_str(), uid_owner, gid_owner);
691 uid_owner, gid_owner);
703 debug_mode_ = getenv(
"__CVMFS_DEBUG_MODE__") != NULL;
711 struct fuse_args *mount_options;
713 if (!mount_options) {
729 for (
unsigned i = 0, s = tokens.size(); i < s; ++i) {
730 options_manager->
ParsePath(tokens[i],
false);
738 fuse_opt_add_arg(mount_options, volname.c_str());
740 fuse_opt_add_arg(mount_options,
"-odaemon_timeout=300");
741 fuse_opt_add_arg(mount_options,
"-onoapplexattr");
745 if (options_manager->
GetValue(
"CVMFS_MOUNT_RW", ¶meter)
746 && options_manager->
IsOn(parameter)) {
747 fuse_opt_add_arg(mount_options,
"-orw");
749 fuse_opt_add_arg(mount_options,
"-oro");
751 fuse_opt_add_arg(mount_options,
"-onodev");
752 if (options_manager->
GetValue(
"CVMFS_SUID", ¶meter)
753 && options_manager->
IsOn(parameter)) {
759 "must be root to mount with suid option");
761 fuse_opt_add_arg(mount_options,
"-osuid");
765 if (options_manager->
GetValue(
"CVMFS_CPU_AFFINITY", ¶meter)) {
770 for (vector<string>::iterator i = cpus.begin(); i != cpus.end(); i++) {
775 const int err = sched_setaffinity(0,
sizeof(mask), &mask);
778 "Setting CPU Affinity failed with error %d", errno);
782 "CPU affinity setting not supported on macOS");
802 options_manager->
Dump().c_str());
807 if (options_manager->
GetValue(
"CVMFS_SYSLOG_LEVEL", ¶meter))
811 if (options_manager->
GetValue(
"CVMFS_SYSLOG_FACILITY", ¶meter))
817 if (options_manager->
GetValue(
"CVMFS_CHECK_PERMISSIONS", ¶meter)) {
818 if (options_manager->
IsOn(parameter)) {
819 fuse_opt_add_arg(mount_options,
"-odefault_permissions");
825 "Mount point %s does not exist",
mount_point_->c_str());
830 if (options_manager->
GetValue(
"CVMFS_NFILES", ¶meter)) {
834 }
else if (retval == -1) {
837 "Failed to set maximum number of open files, "
838 "insufficient permissions");
841 unsigned soft_limit, hard_limit;
844 "Failed to set requested number of open files, "
845 "using maximum number %u",
847 if (hard_limit > soft_limit) {
854 if (options_manager->
GetValue(
"CVMFS_OOM_SCORE_ADJ", ¶meter)) {
855 const string proc_path =
857 const int fd_oom = open(proc_path.c_str(), O_WRONLY);
863 SafeWrite(fd_oom, parameter.data(), parameter.length());
866 "failed to set OOM score adjustment to %s", parameter.c_str());
873 if (options_manager->
GetValue(
"CVMFS_SYSTEMD_NOKILL", ¶meter)
874 && options_manager->
IsOn(parameter)) {
883 "Failed to grab mountpoint %s (%d)",
mount_point_->c_str(),
889 #if CVMFS_USE_LIBFUSE != 2
890 int premount_fd = -1;
899 "Failed to stat mountpoint %s (%d)",
mount_point_->c_str(),
903 premount_fd = open(
"/dev/fuse", O_RDWR);
904 if (premount_fd == -1) {
906 "Failed to open /dev/fuse (%d)", errno);
911 opts,
sizeof(opts),
"fd=%i,rootmode=%o,user_id=0,group_id=0%s%s",
912 premount_fd, info.st_mode & S_IFMT,
914 ?
",default_permissions"
917 unsigned long flags = MS_NOSUID | MS_NODEV | MS_RELATIME;
921 if (mount(
"cvmfs2",
mount_point_->c_str(),
"fuse", flags, opts) == -1) {
923 "Failed to mount -t fuse -o %s cvmfs2 %s (%d)", opts,
930 int fd_mountinfo = -1;
939 "Failed to drop credentials");
951 if (options_manager->
GetValue(
"CVMFS_USYSLOG", ¶meter))
957 "CernVM-FS: running in single threaded mode");
961 "CernVM-FS: running in debug mode");
964 #ifndef FUSE_CAP_POSIX_ACL
965 if (options_manager->
GetValue(
"CVMFS_ENFORCE_ACLS", ¶meter)
966 && options_manager->
IsOn(parameter)) {
968 "CernVM-FS: ACL support requested but not available in this "
969 "version of libfuse");
977 if (options_manager->
GetValue(
"CVMFS_RELOAD_SOCKETS", ¶meter))
983 "Failed to initialize loader socket");
990 if (options_manager->
GetValue(
"CVMFS_FUSE3_MAX_THREADS", ¶meter)) {
993 if (options_manager->
GetValue(
"CVMFS_FUSE3_IDLE_THREADS", ¶meter)) {
996 #ifdef CVMFS_ENABLE_FUSE3_LOOP_CONFIG
1008 "CernVM-FS: ignoring fuse3 thread settings (libfuse too old)");
1013 delete options_manager;
1014 options_manager = NULL;
1017 #if CVMFS_USE_LIBFUSE == 2
1018 struct fuse_chan *channel;
1028 "CernVM-FS: loading Fuse module... ");
1038 "\nCernVM-FS: repository %s already mounted on %s",
1055 const bool retrievable =
true;
1058 "failed to re-gain root permissions for mounting");
1066 struct fuse_lowlevel_ops loader_operations;
1068 #if (FUSE_VERSION >= 29)
1070 loader_operations.forget_multi = stub_forget_multi;
1073 #if CVMFS_USE_LIBFUSE == 2
1074 channel = fuse_mount(
mount_point_->c_str(), mount_options);
1077 "failed to create Fuse channel");
1083 session = fuse_lowlevel_new(mount_options, &loader_operations,
1084 sizeof(loader_operations), NULL);
1087 "failed to create Fuse session");
1095 session = fuse_session_new(mount_options, &loader_operations,
1096 sizeof(loader_operations), NULL);
1099 "failed to create Fuse session");
1104 if (premount_fd >= 0) {
1105 char premount_str[64];
1106 snprintf(premount_str,
sizeof(premount_str),
"/dev/fd/%d", premount_fd);
1107 retval = fuse_session_mount(session, premount_str);
1109 retval = fuse_session_mount(session,
mount_point_->c_str());
1113 "failed to mount file system");
1125 "failed to drop permissions after mounting");
1133 fd_mountinfo = open(
"/proc/self/mountinfo", O_RDONLY);
1134 if (fd_mountinfo > 0) {
1136 while (
GetLineFd(fd_mountinfo, &line)) {
1137 std::vector<std::string> tokens =
SplitString(line,
' ');
1138 if (tokens.size() < 5)
1143 for (; i < tokens.size(); ++i) {
1144 if (tokens[i] ==
"-")
1147 if (tokens.size() < i + 3)
1149 if (tokens[i + 2] !=
"cvmfs2")
1154 close(fd_mountinfo);
1170 retval = fuse_set_signal_handlers(session);
1172 #if CVMFS_USE_LIBFUSE == 2
1173 fuse_session_add_chan(session, channel);
1176 retval = fuse_session_loop(session);
1178 #if CVMFS_USE_LIBFUSE == 2
1179 retval = fuse_session_loop_mt(session);
1181 #ifdef CVMFS_ENABLE_FUSE3_LOOP_CONFIG
1182 struct fuse_loop_config *fuse_loop_cfg = fuse_loop_cfg_create();
1184 fuse_loop_cfg_set_clone_fd(fuse_loop_cfg, 1);
1193 retval = fuse_session_loop_mt(session, fuse_loop_cfg);
1194 fuse_loop_cfg_destroy(fuse_loop_cfg);
1196 retval = fuse_session_loop_mt(session, 1 );
1197 #endif // CVMFS_ENABLE_FUSE3_LOOP_CONFIG
1206 #if CVMFS_USE_LIBFUSE == 2
1207 fuse_remove_signal_handlers(session);
1208 fuse_session_remove_chan(channel);
1209 fuse_session_destroy(session);
1214 fuse_remove_signal_handlers(session);
1215 fuse_session_unmount(session);
1216 fuse_session_destroy(session);
1218 fuse_opt_free_args(mount_options);
1219 delete mount_options;
1221 mount_options = NULL;
1241 #if CVMFS_USE_LIBFUSE != 2
1242 if (premount_fd >= 0) {
1256 #if CVMFS_USE_LIBFUSE != 2
1257 if (premount_fd >= 0) {
1260 "failed to re-gain root permissions for umounting");
1264 "failed to umount %s (%d)",
mount_point_->c_str(), errno);
1274 repository_name_ = NULL;
1286 g_cvmfs_stub_exports->fn_main =
FuseMain;
1290 delete g_cvmfs_stub_exports;
1291 g_cvmfs_stub_exports = NULL;
bool MakeCacheDirectories(const std::string &path, const mode_t mode)
bool simple_options_parsing_
std::string repository_name
static void stub_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
void SetLogSyslogFacility(const int local_facility)
static void stub_destroy(void *userdata)
const char * Code2Ascii(const ObjectFetcherFailures::Failures error)
void SetLogSyslogLevel(const int level)
static void stub_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
virtual void ParsePath(const std::string &config_file, const bool external)=0
Session * session() const
static struct fuse_opt cvmfs_array_opts[]
Failures Reload(const int fd_progress, const bool stop_and_go, const ReloadMode reload_mode)
static CvmfsExports * LoadLibrary(const bool debug_mode, LoaderExports *loader_exports)
static void stub_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
int simple_options_parsing
CVMFS_EXPORT const LogSource const int mask
int(* fnAltProcessFlavor)(int argc, char **argv)
int FuseMain(int argc, char *argv[])
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
string Trim(const string &raw, bool trim_newline)
string JoinStrings(const vector< string > &strings, const string &joint)
static void stub_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
bool IsOn(const std::string ¶m_value) const
bool SafeWrite(int fd, const void *buf, size_t nbyte)
void SendMsg2Socket(const int fd, const std::string &msg)
assert((mem||(size==0))&&"Out Of Memory")
void ** fuse_channel_or_session
MountPoint * mount_point_
std::string loader_version
void SetLogMicroSyslog(const std::string &filename)
void ParseDefault(const std::string &fqrn)
bool(* fnSaveState)(const int fd_progress, StateList *saved_states)
struct cvmcache_object_info __attribute__
static void stub_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
int SetLimitNoFile(unsigned limit_nofile)
static void stub_statfs(fuse_req_t req, fuse_ino_t ino)
int64_t String2Int64(const string &value)
void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit)
static void stub_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
static void stub_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
static void stub_readlink(fuse_req_t req, fuse_ino_t ino)
static void stub_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
bool CheckPremounted(const std::string &mountpoint)
vector< string > SplitString(const string &str, char delim)
bool(* fnRestoreState)(const int fd_progress, const StateList &saved_states)
#define CVMFS_OPT(t, p, v)
static fuse_args * ParseCmdLine(int argc, char *argv[])
const loader::LoaderExports * loader_exports_
struct fuse_lowlevel_ops cvmfs_operations
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
void WaitForSignal(int signum)
static void SetFuseOperations(struct fuse_lowlevel_ops *loader_operations)
int(* fnInit)(const LoaderExports *loader_exports)
bool simple_options_parsing
static int Init(const loader::LoaderExports *loader_exports)
static void stub_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
bool(* fnMaintenanceMode)(const int fd_progress)
void(* fnFreeSavedState)(const int fd_progress, const StateList &saved_states)
string StringifyInt(const int64_t value)
int MainReload(const std::string &socket_path, const bool stop_and_go, const bool debug)
bool GetValue(const std::string &key, std::string *value) const
bool DirectoryExists(const std::string &path)
static void stub_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
bool GetLineFd(const int fd, std::string *line)
static void CloseLibrary()
uint64_t String2Uint64(const string &value)
std::string(* fnGetErrorMsg)()
bool SwitchCredentials(const uid_t uid, const gid_t gid, const bool temporarily)
static int ParseFuseOptions(void *data __attribute__((unused)), const char *arg, int key, struct fuse_args *outargs)
static void stub_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
static bool MatchFuseOption(const fuse_args *mount_options, const char *opt)
#define CVMFS_SWITCH(t, p)
void SetLogSyslogPrefix(const std::string &prefix)
std::string MakeCanonicalPath(const std::string &path)
static void stub_init(void *userdata, struct fuse_conn_info *conn)
CvmfsExports * cvmfs_exports_
static void * OpenLibrary(const string &path)
void BlockSignal(int signum)
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
std::string * repository_name_