mirror of https://github.com/trapexit/mergerfs.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							215 lines
						
					
					
						
							4.2 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							215 lines
						
					
					
						
							4.2 KiB
						
					
					
				
								/*
							 | 
						|
								  Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
							 | 
						|
								
							 | 
						|
								  Permission to use, copy, modify, and/or distribute this software for any
							 | 
						|
								  purpose with or without fee is hereby granted, provided that the above
							 | 
						|
								  copyright notice and this permission notice appear in all copies.
							 | 
						|
								
							 | 
						|
								  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
							 | 
						|
								  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
							 | 
						|
								  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
							 | 
						|
								  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
							 | 
						|
								  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
							 | 
						|
								  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
							 | 
						|
								  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
							 | 
						|
								*/
							 | 
						|
								
							 | 
						|
								#include "rnd.hpp"
							 | 
						|
								
							 | 
						|
								#include <grp.h>
							 | 
						|
								#include <pwd.h>
							 | 
						|
								#include <stdlib.h>
							 | 
						|
								#include <sys/types.h>
							 | 
						|
								#include <unistd.h>
							 | 
						|
								
							 | 
						|
								#if defined __linux__ and UGID_USE_RWLOCK == 0
							 | 
						|
								# include <sys/syscall.h>
							 | 
						|
								#elif __APPLE__
							 | 
						|
								# 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<pthread_t,GIDCache*> g_REGISTERED_CACHES;
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								inline
							 | 
						|
								bool
							 | 
						|
								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);
							 | 
						|
								
							 | 
						|
								  bool inserted;
							 | 
						|
								  pthread_t pthid;
							 | 
						|
								
							 | 
						|
								  pthid = pthread_self();
							 | 
						|
								  inserted = g_REGISTERED_CACHES.emplace(pthid,this).second;
							 | 
						|
								
							 | 
						|
								  assert(inserted == true);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								inline
							 | 
						|
								GIDRecord *
							 | 
						|
								GIDCache::begin(void)
							 | 
						|
								{
							 | 
						|
								  return &recs[0];
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								inline
							 | 
						|
								GIDRecord *
							 | 
						|
								GIDCache::end(void)
							 | 
						|
								{
							 | 
						|
								  return &recs[size];
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								inline
							 | 
						|
								GIDRecord *
							 | 
						|
								GIDCache::allocrec(void)
							 | 
						|
								{
							 | 
						|
								  if(size == MAXRECS)
							 | 
						|
								    return &recs[RND::rand64(MAXRECS)];
							 | 
						|
								  else
							 | 
						|
								    return &recs[size++];
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								inline
							 | 
						|
								GIDRecord *
							 | 
						|
								GIDCache::lower_bound(GIDRecord   *begin,
							 | 
						|
								                      GIDRecord   *end,
							 | 
						|
								                      const uid_t  uid)
							 | 
						|
								{
							 | 
						|
								  int step;
							 | 
						|
								  int count;
							 | 
						|
								  GIDRecord *iter;
							 | 
						|
								
							 | 
						|
								  count = std::distance(begin,end);
							 | 
						|
								  while(count > 0)
							 | 
						|
								    {
							 | 
						|
								      iter = begin;
							 | 
						|
								      step = count / 2;
							 | 
						|
								      std::advance(iter,step);
							 | 
						|
								      if(iter->uid < uid)
							 | 
						|
								        {
							 | 
						|
								          begin  = ++iter;
							 | 
						|
								          count -= step + 1;
							 | 
						|
								        }
							 | 
						|
								      else
							 | 
						|
								        {
							 | 
						|
								          count = step;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								  return begin;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								int
							 | 
						|
								_getgrouplist(const char  *user,
							 | 
						|
								              const gid_t  group,
							 | 
						|
								              gid_t       *groups,
							 | 
						|
								              int         *ngroups)
							 | 
						|
								{
							 | 
						|
								#if __APPLE__
							 | 
						|
								  return ::getgrouplist(user,group,(int*)groups,ngroups);
							 | 
						|
								#else
							 | 
						|
								  return ::getgrouplist(user,group,groups,ngroups);
							 | 
						|
								#endif
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								GIDRecord *
							 | 
						|
								GIDCache::cache(const uid_t uid,
							 | 
						|
								                const gid_t gid)
							 | 
						|
								{
							 | 
						|
								  int rv;
							 | 
						|
								  char buf[4096];
							 | 
						|
								  struct passwd pwd;
							 | 
						|
								  struct passwd *pwdrv;
							 | 
						|
								  GIDRecord *rec;
							 | 
						|
								
							 | 
						|
								  rec = allocrec();
							 | 
						|
								
							 | 
						|
								  rec->uid = uid;
							 | 
						|
								  rv = ::getpwuid_r(uid,&pwd,buf,sizeof(buf),&pwdrv);
							 | 
						|
								  if(pwdrv != NULL && rv == 0)
							 | 
						|
								    {
							 | 
						|
								      rec->size = 0;
							 | 
						|
								      ::_getgrouplist(pwd.pw_name,gid,NULL,&rec->size);
							 | 
						|
								      rec->size = std::min(MAXGIDS,rec->size);
							 | 
						|
								      rv = ::_getgrouplist(pwd.pw_name,gid,rec->gids,&rec->size);
							 | 
						|
								      if(rv == -1)
							 | 
						|
								        {
							 | 
						|
								          rec->gids[0] = gid;
							 | 
						|
								          rec->size    = 1;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								  return rec;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								inline
							 | 
						|
								int
							 | 
						|
								setgroups(const GIDRecord *rec)
							 | 
						|
								{
							 | 
						|
								#if defined __linux__ and UGID_USE_RWLOCK == 0
							 | 
						|
								# if defined SYS_setgroups32
							 | 
						|
								  return ::syscall(SYS_setgroups32,rec->size,rec->gids);
							 | 
						|
								# else
							 | 
						|
								  return ::syscall(SYS_setgroups,rec->size,rec->gids);
							 | 
						|
								# endif
							 | 
						|
								#else
							 | 
						|
								  return ::setgroups(rec->size,rec->gids);
							 | 
						|
								#endif
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								int
							 | 
						|
								GIDCache::initgroups(const uid_t uid,
							 | 
						|
								                     const gid_t gid)
							 | 
						|
								{
							 | 
						|
								  int rv;
							 | 
						|
								  GIDRecord *rec;
							 | 
						|
								
							 | 
						|
								  if(invalidate)
							 | 
						|
								    {
							 | 
						|
								      size       = 0;
							 | 
						|
								      invalidate = false;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								  rec = lower_bound(begin(),end(),uid);
							 | 
						|
								  if(rec == end() || rec->uid != uid)
							 | 
						|
								    {
							 | 
						|
								      rec = cache(uid,gid);
							 | 
						|
								      rv = ::setgroups(rec);
							 | 
						|
								      std::sort(begin(),end());
							 | 
						|
								    }
							 | 
						|
								  else
							 | 
						|
								    {
							 | 
						|
								      rv = ::setgroups(rec);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								  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;
							 | 
						|
								}
							 |