Browse Source

Add ability to invalidate gid cache on demand

SIGUSR2 or ioctl
pull/1227/head
Antonio SJ Musumeci 1 year ago
parent
commit
0aafdefc18
  1. 1
      libfuse/lib/fuse.c
  2. 6
      src/fuse_ioctl.cpp
  3. 77
      src/gidcache.cpp
  4. 38
      src/gidcache.hpp
  5. 2
      src/mergerfs.cpp
  6. 2
      src/ugid.cpp
  7. 6
      src/ugid_linux.hpp
  8. 6
      src/ugid_linux.icpp

1
libfuse/lib/fuse.c

@ -753,7 +753,6 @@ find_node(struct fuse *f,
if(f->conf.remember) if(f->conf.remember)
inc_nlookup(node); inc_nlookup(node);
printf("hash_name = %s\n",name);
if(hash_name(f,node,parent,name) == -1) if(hash_name(f,node,parent,name) == -1)
{ {
free_node(f,node); free_node(f,node);

6
src/fuse_ioctl.cpp

@ -24,6 +24,7 @@
#include "fs_ioctl.hpp" #include "fs_ioctl.hpp"
#include "fs_open.hpp" #include "fs_open.hpp"
#include "fs_path.hpp" #include "fs_path.hpp"
#include "gidcache.hpp"
#include "str.hpp" #include "str.hpp"
#include "ugid.hpp" #include "ugid.hpp"
@ -46,6 +47,8 @@ typedef char IOCTL_BUF[4096];
#define IOCTL_GC _IO(IOCTL_APP_TYPE,1) #define IOCTL_GC _IO(IOCTL_APP_TYPE,1)
#define IOCTL_GC1 _IO(IOCTL_APP_TYPE,2) #define IOCTL_GC1 _IO(IOCTL_APP_TYPE,2)
#define IOCTL_INVALIDATE_ALL_NODES _IO(IOCTL_APP_TYPE,3) #define IOCTL_INVALIDATE_ALL_NODES _IO(IOCTL_APP_TYPE,3)
#define IOCTL_INVALIDATE_GID_CACHE _IO(IOCTL_APP_TYPE,4)
// From linux/btrfs.h // From linux/btrfs.h
#define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_IOCTL_MAGIC 0x94
@ -346,6 +349,9 @@ namespace l
case IOCTL_INVALIDATE_ALL_NODES: case IOCTL_INVALIDATE_ALL_NODES:
fuse_invalidate_all_nodes(); fuse_invalidate_all_nodes();
return 0; return 0;
case IOCTL_INVALIDATE_GID_CACHE:
GIDCache::invalidate_all_caches();
break;
} }
return -ENOTTY; return -ENOTTY;

77
src/gidcache.cpp

@ -26,35 +26,59 @@
# include <sys/param.h> # include <sys/param.h>
#endif #endif
#include <cassert>
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
#include <unordered_map>
#include <mutex>
#include "gidcache.hpp" #include "gidcache.hpp"
std::mutex g_REGISTERED_CACHES_MUTEX;
std::unordered_map<pid_t,GIDCache*> g_REGISTERED_CACHES;
inline inline
bool bool
gid_t_rec::operator<(const struct gid_t_rec &b) const
GIDRecord::operator<(const struct GIDRecord &b) const
{ {
return uid < b.uid; return uid < b.uid;
} }
GIDCache::GIDCache()
: invalidate(false),
size(0),
recs()
{
std::lock_guard<std::mutex> guard(g_REGISTERED_CACHES_MUTEX);
pid_t tid;
bool inserted;
tid = ::gettid();
inserted = g_REGISTERED_CACHES.emplace(tid,this).second;
assert(inserted == true);
}
inline inline
gid_t_rec *
gid_t_cache::begin(void)
GIDRecord *
GIDCache::begin(void)
{ {
return recs;
return &recs[0];
} }
inline inline
gid_t_rec *
gid_t_cache::end(void)
GIDRecord *
GIDCache::end(void)
{ {
return recs + size;
return &recs[size];
} }
inline inline
gid_t_rec *
gid_t_cache::allocrec(void)
GIDRecord *
GIDCache::allocrec(void)
{ {
if(size == MAXRECS) if(size == MAXRECS)
return &recs[rand() % MAXRECS]; return &recs[rand() % MAXRECS];
@ -63,14 +87,14 @@ gid_t_cache::allocrec(void)
} }
inline inline
gid_t_rec *
gid_t_cache::lower_bound(gid_t_rec *begin,
gid_t_rec *end,
GIDRecord *
GIDCache::lower_bound(GIDRecord *begin,
GIDRecord *end,
const uid_t uid) const uid_t uid)
{ {
int step; int step;
int count; int count;
gid_t_rec *iter;
GIDRecord *iter;
count = std::distance(begin,end); count = std::distance(begin,end);
while(count > 0) while(count > 0)
@ -106,15 +130,15 @@ _getgrouplist(const char *user,
#endif #endif
} }
gid_t_rec *
gid_t_cache::cache(const uid_t uid,
GIDRecord *
GIDCache::cache(const uid_t uid,
const gid_t gid) const gid_t gid)
{ {
int rv; int rv;
char buf[4096]; char buf[4096];
struct passwd pwd; struct passwd pwd;
struct passwd *pwdrv; struct passwd *pwdrv;
gid_t_rec *rec;
GIDRecord *rec;
rec = allocrec(); rec = allocrec();
@ -139,7 +163,7 @@ gid_t_cache::cache(const uid_t uid,
static static
inline inline
int int
setgroups(const gid_t_rec *rec)
setgroups(const GIDRecord *rec)
{ {
#if defined __linux__ and UGID_USE_RWLOCK == 0 #if defined __linux__ and UGID_USE_RWLOCK == 0
# if defined SYS_setgroups32 # if defined SYS_setgroups32
@ -153,11 +177,17 @@ setgroups(const gid_t_rec *rec)
} }
int int
gid_t_cache::initgroups(const uid_t uid,
GIDCache::initgroups(const uid_t uid,
const gid_t gid) const gid_t gid)
{ {
int rv; int rv;
gid_t_rec *rec;
GIDRecord *rec;
if(invalidate)
{
size = 0;
invalidate = false;
}
rec = lower_bound(begin(),end(),uid); rec = lower_bound(begin(),end(),uid);
if(rec == end() || rec->uid != uid) if(rec == end() || rec->uid != uid)
@ -173,3 +203,12 @@ gid_t_cache::initgroups(const uid_t uid,
return rv; return rv;
} }
void
GIDCache::invalidate_all_caches()
{
std::lock_guard<std::mutex> guard(g_REGISTERED_CACHES_MUTEX);
for(auto &p : g_REGISTERED_CACHES)
p.second->invalidate = true;
}

38
src/gidcache.hpp

@ -19,37 +19,55 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <array>
#define MAXGIDS 32 #define MAXGIDS 32
#define MAXRECS 256 #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; uid_t uid;
int size; int size;
gid_t gids[MAXGIDS]; gid_t gids[MAXGIDS];
bool bool
operator<(const struct gid_t_rec &b) const;
operator<(const struct GIDRecord &b) const;
}; };
struct gid_t_cache
struct GIDCache
{ {
public: public:
GIDCache();
public:
bool invalidate;
size_t size; size_t size;
gid_t_rec recs[MAXRECS];
std::array<GIDRecord,MAXRECS> recs;
private: 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,
GIDRecord *begin(void);
GIDRecord *end(void);
GIDRecord *allocrec(void);
GIDRecord *lower_bound(GIDRecord *begin,
GIDRecord *end,
const uid_t uid); const uid_t uid);
gid_t_rec * cache(const uid_t uid,
GIDRecord *cache(const uid_t uid,
const gid_t gid); const gid_t gid);
public: public:
int int
initgroups(const uid_t uid, initgroups(const uid_t uid,
const gid_t gid); const gid_t gid);
public:
static void invalidate_all_caches();
}; };

2
src/mergerfs.cpp

@ -25,6 +25,7 @@
#include "procfs_get_name.hpp" #include "procfs_get_name.hpp"
#include "resources.hpp" #include "resources.hpp"
#include "strvec.hpp" #include "strvec.hpp"
#include "gidcache.hpp"
#include "fuse_access.hpp" #include "fuse_access.hpp"
#include "fuse_bmap.hpp" #include "fuse_bmap.hpp"
@ -218,6 +219,7 @@ namespace l
{ {
syslog_info("Received SIGUSR2 - triggering thorough gc"); syslog_info("Received SIGUSR2 - triggering thorough gc");
fuse_gc(); fuse_gc();
GIDCache::invalidate_all_caches();
} }
static static

2
src/ugid.cpp

@ -28,7 +28,7 @@ namespace ugid
initgroups(const uid_t uid_, initgroups(const uid_t uid_,
const gid_t gid_) const gid_t gid_)
{ {
static thread_local gid_t_cache cache = {0};
static thread_local GIDCache cache;
cache.initgroups(uid_,gid_); cache.initgroups(uid_,gid_);
} }

6
src/ugid_linux.hpp

@ -53,9 +53,9 @@
namespace ugid 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 struct Set
{ {

6
src/ugid_linux.icpp

@ -23,9 +23,9 @@
namespace ugid 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 void
init() init()

Loading…
Cancel
Save