//------------------------------------------------------------------------------ //! @file eosfuse.hh //! @author Andreas-Joachim Peters CERN //! @brief EOS C++ Fuse low-level implementation //------------------------------------------------------------------------------ /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #ifndef FUSE_EOSFUSE_HH_ #define FUSE_EOSFUSE_HH_ #include "misc/MacOSXHelper.hh" #include "common/AssistedThread.hh" #include "common/LinuxTotalMem.hh" #include "common/Murmur3.hh" #include "stat/Stat.hh" #include "md/md.hh" #include "cap/cap.hh" #include "data/data.hh" #include "backend/backend.hh" #include "kv/kv.hh" #include "kv/NoKV.hh" #include "llfusexx.hh" #include "auth/CredentialFinder.hh" #include "misc/Track.hh" #include "misc/FuseId.hh" #include "misc/stringTS.hh" #include "submount/SubMount.hh" #include #include #include #include #include #include #include #include // PROTOBUF protocol version announced via heartbeats and attached to URLs by the backend #define FUSEPROTOCOLVERSION eos::fusex::heartbeat::PROTOCOLV5 class EosFuse : public llfusexx::FuseBase { public: static EosFuse& instance() { static EosFuse i; return i; } EosFuse(); virtual ~EosFuse(); std::string UsageGet(); std::string UsageSet(); std::string UsageMount(); std::string UsageHelp(); int run(int argc, char* argv[], void* userdata); static void umounthandler(int sig, siginfo_t* si, void* unused); static void init(void* userdata, struct fuse_conn_info* conn); static void destroy(void* userdata); static void getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); static void setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, struct fuse_file_info* fi); static void lookup(fuse_req_t req, fuse_ino_t parent, const char* name); static int listdir(fuse_req_t req, fuse_ino_t ino, metad::shared_md& md, double& lifetime); static void opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); static void readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi); static void readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi, bool plus); static void readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi); static void releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); static void statfs(fuse_req_t req, fuse_ino_t ino); static void mknod(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, dev_t rdev); static void mkdir(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode); static void rm(fuse_req_t req, fuse_ino_t parent, const char* name); static void unlink(fuse_req_t req, fuse_ino_t parent, const char* name); static void rmdir(fuse_req_t req, fuse_ino_t parent, const char* name); #ifdef USE_FUSE3 static void rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t newparent, const char* newname, unsigned int flags); #else static void rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t newparent, const char* newname); #endif static void access(fuse_req_t req, fuse_ino_t ino, int mask); static void open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); static void create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, struct fuse_file_info* fi); static void read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi); static void write(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, off_t off, struct fuse_file_info* fi); static void release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); static void fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info* fi); static void forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); static int _forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); #ifdef USE_FUSE3 static void forget_multi(fuse_req_t req, size_t count, struct fuse_forget_data *forgets); #endif static void flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); #ifdef __APPLE__ static void getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size, uint32_t position); #else static void getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size); #endif #ifdef __APPLE__ static void setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, size_t size, int flags, uint32_t position); #else static void setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, size_t size, int flags); #endif static void listxattr(fuse_req_t req, fuse_ino_t ino, size_t size); static void removexattr(fuse_req_t req, fuse_ino_t ino, const char* name); static void readlink(fuse_req_t req, fuse_ino_t ino); static void link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char* newname); static void symlink(fuse_req_t req, const char* link, fuse_ino_t parent, const char* name); static void getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, struct flock* lock); static void setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, struct flock* lock, int sleep); #if ( FUSE_VERSION > 28 ) static void flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op); #endif metad mds; ::data datas; cap caps; backend mdbackend; static EosFuse& Instance() { return *sEosFuse; } fuse_session* Session() { return fusesession; } #ifndef USE_FUSE3 fuse_chan* Channel() { return fusechan; } #endif std::string Prefix(std::string path); typedef struct cfg { std::string name; std::string hostport; std::string remotemountdir; std::string localmountdir; std::string statfilesuffix; std::string statfilepath; std::string logfilepath; std::string mdcachedir; std::string mdcachedir_unlink; std::string mqtargethost; std::string mqidentity; std::string mqname; std::string clienthost; std::string clientuuid; std::string ssskeytab; std::string appname; std::string encryptionkey; typedef struct options { int debug; int debuglevel; bool jsonstats; int libfusethreads; int foreground; int automounted; int md_kernelcache; int enable_backtrace; double md_kernelcache_enoent_timeout; double md_backend_timeout; double md_backend_put_timeout; int data_kernelcache; int rename_is_sync; int rmdir_is_sync; int global_flush; int flush_wait_umount; int flush_wait_open; enum eFLUSH_WAIT_OPEN { kWAIT_FLUSH_NEVER = 0, // if a file is updated/created - flush will not wait to open it kWAIT_FLUSH_ON_UPDATE = 1, // if a file is updated - flush will wait to open it kWAIT_FLUSH_ON_CREATE = 2 // if a file is created - flush will wait to open it }; size_t flush_wait_open_size; bool writebackcache; int global_locking; uint64_t fdlimit; int rm_rf_protect_levels; int rm_rf_bulk; int show_tree_size; int free_md_asap; int cpu_core_affinity; mode_t overlay_mode; mode_t x_ok; int no_xattr; int no_eos_xattr_listing; int no_hardlinks; uint32_t nocache_graceperiod; int leasetime; int write_size_flush_interval; int submounts; int inmemory_inodes; bool flock; bool hide_versions; std::vector no_fsync_suffixes; std::vector nowait_flush_executables; bool protect_directory_symlink_loops; } options_t; typedef struct recovery { int read; int write; int read_open; int write_open; int read_open_noserver; int write_open_noserver; size_t read_open_noserver_retrywindow; size_t write_open_noserver_retrywindow; } recovery_t; typedef struct fuzzing { size_t open_async_submit; size_t open_async_return; size_t read_async_return; bool open_async_submit_fatal; bool open_async_return_fatal; } fuzzing_t; typedef struct inlining { uint64_t max_size; std::string default_compressor; } inlining_t; recovery_t recovery; options_t options; inlining_t inliner; fuzzing_t fuzzing; CredentialConfig auth; } cfg_t; cfg_t& Config() { return config; } Track& Tracker() { return tracker; } SubMount& Mounter() { return mounter; } const char* umountcall() { return umount_system_line.c_str(); } static std::string dump(fuse_id id, fuse_ino_t ino, struct fuse_file_info* fi, int rc, std::string name = "") { char s[1024]; char ebuf[1024]; ebuf[0] = 0; if (strerror_r(rc, ebuf, sizeof(ebuf))) { snprintf(ebuf, sizeof(ebuf), "???"); } snprintf(s, 1024, "rc=%02d uid=%05d gid=%05d pid=%05d ino=%#lx fh=%#lx name=%s", rc, id.uid, id.gid, id.pid, ino, fi ? fi->fh : 0, name.c_str()); return s; } typedef struct opendir_fh { typedef std::vector ChildSet; typedef std::map ChildMap; opendir_fh() { pmd_mtime.tv_sec = pmd_mtime.tv_nsec = 0; } metad::shared_md md; ChildSet readdir_items; ChildMap pmd_children; struct reply_buf { char blob[65536]; char* ptr; off_t size; reply_buf() { reset(); } void reset() { ptr = blob; size = 0; } char* buffer() { return blob; } }; struct timespec pmd_mtime; struct reply_buf b; double lifetime; struct timespec opendir_time; XrdSysMutex items_lock; } opendir_t; static int readdir_filler(fuse_req_t req, opendir_t* md, mode_t&pmd_mode, uint64_t&pmd_id); void getHbStat(eos::fusex::statistics&); kv* getKV() { return mKV.get(); } cap& getCap() { return caps; } void cleanup(fuse_ino_t ino) { return mds.cleanup(ino); } void TrackMgm(const std::string& lasturl); stringTS statsout; int truncateLogFile() { if (!fstderr) { return 0; } fflush(fstderr); return ftruncate(fileno(fstderr), (off_t) 0); } size_t sizeLogFile() { struct stat buf; if (!fstderr) { return 0; } if (!fstat(fileno(fstderr), &buf)) { return buf.st_size; } else { return 0; } } void shrinkLogFile() { if (!fstderr) { return ; } const size_t maxsize = 4*1024ll*1024ll*1024ll; // 4G if ( sizeLogFile() > maxsize) { ftruncate(fileno(fstderr), maxsize/2); eos_static_crit("logfile has been truncated back to %lu bytes - exceeded %lu bytes", maxsize/2, maxsize); } } bool Trace() { return mTrace; } void SetTrace(bool t) { mTrace = t;} protected: private: static bool isRecursiveRm(fuse_req_t req, bool forced = false, bool notverbose = false); static void Merge(Json::Value& a, Json::Value& b) { if (!a.isObject() || !b.isObject()) return; for (const auto& key : b.getMemberNames()) { if(a[key].type() == Json::objectValue && b[key].type() == Json::objectValue) { Merge(a[key], b[key]); } else { a[key] = b[key]; } } } bool mTrace; Track tracker; SubMount mounter; cfg_t config; stringTS lastMgmHostPort; std::unique_ptr mKV; Stat fusestat; FILE* fstderr; Stat& getFuseStat() { return fusestat; } metad::mdstat& getMdStat() { return mds.stats(); } eos::common::LinuxTotalMem meminfo; XrdSysMutex statsoutmutex; static EosFuse* sEosFuse; struct fuse_session* fusesession; #ifndef USE_FUSE3 struct fuse_chan* fusechan; #endif std::atomic aRecoveryOk; std::atomic aRecoveryFail; AssistedThread tDumpStatistic; AssistedThread tStatCirculate; AssistedThread tMetaCacheFlush; AssistedThread tMetaStackFree; AssistedThread tMetaCommunicate; AssistedThread tMetaCallback; AssistedThread tCapFlush; std::string umount_system_line; void DumpStatistic(ThreadAssistant& assistant); void StatCirculate(ThreadAssistant& assistant); }; #endif /* FUSE_EOSFUSE_HH_ */