Skip to content

Commit

Permalink
Add ability to invalidate gid cache on demand
Browse files Browse the repository at this point in the history
SIGUSR2 or ioctl
  • Loading branch information
trapexit committed Aug 14, 2023
1 parent 8b769df commit 0aafdef
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 44 deletions.
1 change: 0 additions & 1 deletion libfuse/lib/fuse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion src/fuse_ioctl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
83 changes: 61 additions & 22 deletions src/gidcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,59 @@
# include <sys/param.h>
#endif

#include <cassert>
#include <cstdlib>
#include <algorithm>
#include <unordered_map>
#include <mutex>


#include "gidcache.hpp"

std::mutex g_REGISTERED_CACHES_MUTEX;
std::unordered_map<pid_t,GIDCache*> 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<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
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];
Expand All @@ -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)
Expand Down Expand Up @@ -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();

Expand All @@ -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
Expand All @@ -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)
Expand All @@ -173,3 +203,12 @@ gid_t_cache::initgroups(const uid_t uid,

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;
}
44 changes: 31 additions & 13 deletions src/gidcache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,55 @@
#include <sys/types.h>
#include <unistd.h>

#include <array>

#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<GIDRecord,MAXRECS> 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();
};
2 changes: 2 additions & 0 deletions src/mergerfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -218,6 +219,7 @@ namespace l
{
syslog_info("Received SIGUSR2 - triggering thorough gc");
fuse_gc();
GIDCache::invalidate_all_caches();
}

static
Expand Down
2 changes: 1 addition & 1 deletion src/ugid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
}
Expand Down
6 changes: 3 additions & 3 deletions src/ugid_linux.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
6 changes: 3 additions & 3 deletions src/ugid_linux.icpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 0aafdef

Please sign in to comment.