From 0aafdefc18c4f99f17a94873977c7dd98d2e1568 Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Sun, 13 Aug 2023 22:55:04 -0500 Subject: [PATCH] Add ability to invalidate gid cache on demand SIGUSR2 or ioctl --- libfuse/lib/fuse.c | 1 - src/fuse_ioctl.cpp | 8 ++++- src/gidcache.cpp | 83 +++++++++++++++++++++++++++++++++------------ src/gidcache.hpp | 44 +++++++++++++++++------- src/mergerfs.cpp | 2 ++ src/ugid.cpp | 2 +- src/ugid_linux.hpp | 6 ++-- src/ugid_linux.icpp | 6 ++-- 8 files changed, 108 insertions(+), 44 deletions(-) diff --git a/libfuse/lib/fuse.c b/libfuse/lib/fuse.c index 2589017d..e2b9e3ba 100644 --- a/libfuse/lib/fuse.c +++ b/libfuse/lib/fuse.c @@ -753,7 +753,6 @@ find_node(struct fuse *f, if(f->conf.remember) inc_nlookup(node); - printf("hash_name = %s\n",name); if(hash_name(f,node,parent,name) == -1) { free_node(f,node); diff --git a/src/fuse_ioctl.cpp b/src/fuse_ioctl.cpp index dca3ea89..d076e481 100644 --- a/src/fuse_ioctl.cpp +++ b/src/fuse_ioctl.cpp @@ -24,6 +24,7 @@ #include "fs_ioctl.hpp" #include "fs_open.hpp" #include "fs_path.hpp" +#include "gidcache.hpp" #include "str.hpp" #include "ugid.hpp" @@ -41,11 +42,13 @@ using std::vector; #endif typedef char IOCTL_BUF[4096]; -#define IOCTL_APP_TYPE 0xDF +#define IOCTL_APP_TYPE 0xDF #define IOCTL_FILE_INFO _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF) #define IOCTL_GC _IO(IOCTL_APP_TYPE,1) #define IOCTL_GC1 _IO(IOCTL_APP_TYPE,2) #define IOCTL_INVALIDATE_ALL_NODES _IO(IOCTL_APP_TYPE,3) +#define IOCTL_INVALIDATE_GID_CACHE _IO(IOCTL_APP_TYPE,4) + // From linux/btrfs.h #define BTRFS_IOCTL_MAGIC 0x94 @@ -346,6 +349,9 @@ namespace l case IOCTL_INVALIDATE_ALL_NODES: fuse_invalidate_all_nodes(); return 0; + case IOCTL_INVALIDATE_GID_CACHE: + GIDCache::invalidate_all_caches(); + break; } return -ENOTTY; diff --git a/src/gidcache.cpp b/src/gidcache.cpp index 14be6d67..9b3f8e91 100644 --- a/src/gidcache.cpp +++ b/src/gidcache.cpp @@ -26,35 +26,59 @@ # include #endif +#include #include #include +#include +#include + #include "gidcache.hpp" +std::mutex g_REGISTERED_CACHES_MUTEX; +std::unordered_map g_REGISTERED_CACHES; + + inline bool -gid_t_rec::operator<(const struct gid_t_rec &b) const +GIDRecord::operator<(const struct GIDRecord &b) const { return uid < b.uid; } +GIDCache::GIDCache() + : invalidate(false), + size(0), + recs() +{ + std::lock_guard guard(g_REGISTERED_CACHES_MUTEX); + + pid_t tid; + bool inserted; + + tid = ::gettid(); + inserted = g_REGISTERED_CACHES.emplace(tid,this).second; + + assert(inserted == true); +} + inline -gid_t_rec * -gid_t_cache::begin(void) +GIDRecord * +GIDCache::begin(void) { - return recs; + return &recs[0]; } inline -gid_t_rec * -gid_t_cache::end(void) +GIDRecord * +GIDCache::end(void) { - return recs + size; + return &recs[size]; } inline -gid_t_rec * -gid_t_cache::allocrec(void) +GIDRecord * +GIDCache::allocrec(void) { if(size == MAXRECS) return &recs[rand() % MAXRECS]; @@ -63,14 +87,14 @@ gid_t_cache::allocrec(void) } inline -gid_t_rec * -gid_t_cache::lower_bound(gid_t_rec *begin, - gid_t_rec *end, - const uid_t uid) +GIDRecord * +GIDCache::lower_bound(GIDRecord *begin, + GIDRecord *end, + const uid_t uid) { int step; int count; - gid_t_rec *iter; + GIDRecord *iter; count = std::distance(begin,end); while(count > 0) @@ -106,15 +130,15 @@ _getgrouplist(const char *user, #endif } -gid_t_rec * -gid_t_cache::cache(const uid_t uid, - const gid_t gid) +GIDRecord * +GIDCache::cache(const uid_t uid, + const gid_t gid) { int rv; char buf[4096]; struct passwd pwd; struct passwd *pwdrv; - gid_t_rec *rec; + GIDRecord *rec; rec = allocrec(); @@ -139,7 +163,7 @@ gid_t_cache::cache(const uid_t uid, static inline int -setgroups(const gid_t_rec *rec) +setgroups(const GIDRecord *rec) { #if defined __linux__ and UGID_USE_RWLOCK == 0 # if defined SYS_setgroups32 @@ -153,11 +177,17 @@ setgroups(const gid_t_rec *rec) } int -gid_t_cache::initgroups(const uid_t uid, - const gid_t gid) +GIDCache::initgroups(const uid_t uid, + const gid_t gid) { int rv; - gid_t_rec *rec; + GIDRecord *rec; + + if(invalidate) + { + size = 0; + invalidate = false; + } rec = lower_bound(begin(),end(),uid); if(rec == end() || rec->uid != uid) @@ -173,3 +203,12 @@ gid_t_cache::initgroups(const uid_t uid, return rv; } + +void +GIDCache::invalidate_all_caches() +{ + std::lock_guard guard(g_REGISTERED_CACHES_MUTEX); + + for(auto &p : g_REGISTERED_CACHES) + p.second->invalidate = true; +} diff --git a/src/gidcache.hpp b/src/gidcache.hpp index d4ac8f70..a611dc6f 100644 --- a/src/gidcache.hpp +++ b/src/gidcache.hpp @@ -19,37 +19,55 @@ #include #include +#include + #define MAXGIDS 32 #define MAXRECS 256 -struct gid_t_rec +// GIDCache is a global, per thread cache of uid to gid + supplemental +// groups mapping for use when threads change credentials. This is +// needed due to the high cost of querying such information. The cache +// instance should always be thread local and live the lifetime of the +// app. The constructor will register the instance so they can each be +// told to invalidate the cache on demand. A second instance on the +// same thread will cause an assert to be triggered. + + +struct GIDRecord { uid_t uid; int size; gid_t gids[MAXGIDS]; bool - operator<(const struct gid_t_rec &b) const; + operator<(const struct GIDRecord &b) const; }; -struct gid_t_cache +struct GIDCache { public: - size_t size; - gid_t_rec recs[MAXRECS]; + GIDCache(); + +public: + bool invalidate; + size_t size; + std::array recs; private: - gid_t_rec * begin(void); - gid_t_rec * end(void); - gid_t_rec * allocrec(void); - gid_t_rec * lower_bound(gid_t_rec *begin, - gid_t_rec *end, - const uid_t uid); - gid_t_rec * cache(const uid_t uid, - const gid_t gid); + GIDRecord *begin(void); + GIDRecord *end(void); + GIDRecord *allocrec(void); + GIDRecord *lower_bound(GIDRecord *begin, + GIDRecord *end, + const uid_t uid); + GIDRecord *cache(const uid_t uid, + const gid_t gid); public: int initgroups(const uid_t uid, const gid_t gid); + +public: + static void invalidate_all_caches(); }; diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index 1a04d824..47d36285 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -25,6 +25,7 @@ #include "procfs_get_name.hpp" #include "resources.hpp" #include "strvec.hpp" +#include "gidcache.hpp" #include "fuse_access.hpp" #include "fuse_bmap.hpp" @@ -218,6 +219,7 @@ namespace l { syslog_info("Received SIGUSR2 - triggering thorough gc"); fuse_gc(); + GIDCache::invalidate_all_caches(); } static diff --git a/src/ugid.cpp b/src/ugid.cpp index 5582ccd4..80385482 100644 --- a/src/ugid.cpp +++ b/src/ugid.cpp @@ -28,7 +28,7 @@ namespace ugid initgroups(const uid_t uid_, const gid_t gid_) { - static thread_local gid_t_cache cache = {0}; + static thread_local GIDCache cache; cache.initgroups(uid_,gid_); } diff --git a/src/ugid_linux.hpp b/src/ugid_linux.hpp index 17a1803e..7a0271f6 100644 --- a/src/ugid_linux.hpp +++ b/src/ugid_linux.hpp @@ -53,9 +53,9 @@ namespace ugid { - extern __thread uid_t currentuid; - extern __thread gid_t currentgid; - extern __thread bool initialized; + extern thread_local uid_t currentuid; + extern thread_local gid_t currentgid; + extern thread_local bool initialized; struct Set { diff --git a/src/ugid_linux.icpp b/src/ugid_linux.icpp index 81889f9b..bd768fe5 100644 --- a/src/ugid_linux.icpp +++ b/src/ugid_linux.icpp @@ -23,9 +23,9 @@ namespace ugid { - __thread uid_t currentuid = 0; - __thread gid_t currentgid = 0; - __thread bool initialized = false; + thread_local uid_t currentuid = 0; + thread_local gid_t currentgid = 0; + thread_local bool initialized = false; void init()