Reengineered permissions cache
							parent
							
								
									7335c9af68
								
							
						
					
					
						commit
						59a21e6110
					
				| 
						 | 
				
			
			@ -81,7 +81,6 @@ struct CACHED_SECURID {
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
struct SECURITY_HEAD {
 | 
			
		||||
	unsigned int first;
 | 
			
		||||
	unsigned int last;
 | 
			
		||||
	struct CACHED_SECURID *first_securid;
 | 
			
		||||
	struct CACHED_SECURID *most_recent_securid;
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +103,7 @@ struct SECURITY_HEAD {
 | 
			
		|||
 | 
			
		||||
struct SECURITY_CACHE {
 | 
			
		||||
	struct SECURITY_HEAD head;
 | 
			
		||||
	struct CACHED_PERMISSIONS cachetable[1]; /* array of variable size */
 | 
			
		||||
	struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */
 | 
			
		||||
} ;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,8 @@
 | 
			
		|||
#define MAPPINGFILE "/NTFS-3G/UserMapping" /* name of mapping file */
 | 
			
		||||
#define LINESZ 120              /* maximum useful size of a mapping line */
 | 
			
		||||
#define CACHE_SECURID_SIZE 16    /* securid cache, size >= 3 and not too big */
 | 
			
		||||
#define CACHE_PERMISSIONS_SIZE 4000  /* think twice before increasing */
 | 
			
		||||
#define CACHE_PERMISSIONS_BITS 6  /* log2 of unitary allocation of permissions */
 | 
			
		||||
#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */
 | 
			
		||||
#define CACHE_LEGACY_SIZE 8    /* legacy cache size, zero or >= 3 and not too big */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
| 
						 | 
				
			
			@ -1601,7 +1602,7 @@ static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
 | 
			
		|||
 *	which should not be too long to be efficient. Its optimal
 | 
			
		||||
 *	size is depends on usage and is hard to determine.
 | 
			
		||||
 *
 | 
			
		||||
 *	CACHED_PERMISSIONS data is kept in an indexed array. Is
 | 
			
		||||
 *	CACHED_PERMISSIONS data is kept in a two-level indexed array. It
 | 
			
		||||
 *	is optimal at the expense of storage. Use of a most-recent-first
 | 
			
		||||
 *	list would save memory and provide similar performances for
 | 
			
		||||
 *	standard usage, but not for file servers with too many file
 | 
			
		||||
| 
						 | 
				
			
			@ -1633,7 +1634,8 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
#if CACHE_LEGACY_SIZE
 | 
			
		||||
	struct CACHED_PERMISSIONS_LEGACY *cachelegacy;
 | 
			
		||||
#endif
 | 
			
		||||
	int i;
 | 
			
		||||
	unsigned int index1;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	cache = (struct SECURITY_CACHE*)NULL;
 | 
			
		||||
		/* create the securid cache first */
 | 
			
		||||
| 
						 | 
				
			
			@ -1665,12 +1667,13 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
				(struct CACHED_PERMISSIONS_LEGACY*)NULL;
 | 
			
		||||
			cachelegacy[CACHE_LEGACY_SIZE - 1].permissions.valid = 0;;
 | 
			
		||||
#endif
 | 
			
		||||
				/* create the first permissions cache entry */
 | 
			
		||||
				/* create the first permissions blocks */
 | 
			
		||||
			index1 = securindex >> CACHE_PERMISSIONS_BITS;
 | 
			
		||||
			cache = (struct SECURITY_CACHE*)
 | 
			
		||||
				ntfs_malloc(sizeof(struct SECURITY_CACHE));
 | 
			
		||||
				ntfs_malloc(sizeof(struct SECURITY_CACHE)
 | 
			
		||||
				      + index1*sizeof(struct CACHED_PERMISSIONS*));
 | 
			
		||||
			if (cache) {
 | 
			
		||||
				cache->head.first = securindex;
 | 
			
		||||
				cache->head.last = securindex;
 | 
			
		||||
				cache->head.last = index1;
 | 
			
		||||
				cache->head.p_reads = 0;
 | 
			
		||||
				cache->head.p_hits = 0;
 | 
			
		||||
				cache->head.p_writes = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1681,7 +1684,9 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
				*scx->pseccache = cache;
 | 
			
		||||
				cache->head.first_securid = cachesecurid;
 | 
			
		||||
				cache->head.most_recent_securid = cachesecurid;
 | 
			
		||||
				cache->cachetable[0].valid = 0;
 | 
			
		||||
				for (i=0; i<=index1; i++)
 | 
			
		||||
					cache->cachetable[i]
 | 
			
		||||
					   = (struct CACHED_PERMISSIONS*)NULL;
 | 
			
		||||
#if CACHE_LEGACY_SIZE
 | 
			
		||||
				cache->head.first_legacy = cachelegacy;
 | 
			
		||||
				cache->head.most_recent_legacy = cachelegacy;
 | 
			
		||||
| 
						 | 
				
			
			@ -1701,12 +1706,19 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
 | 
			
		||||
static void free_caches(struct SECURITY_CONTEXT *scx)
 | 
			
		||||
{
 | 
			
		||||
	if (*scx->pseccache) {
 | 
			
		||||
		free((*scx->pseccache)->head.first_securid);
 | 
			
		||||
	unsigned int index1;
 | 
			
		||||
	struct SECURITY_CACHE *pseccache;
 | 
			
		||||
 | 
			
		||||
	pseccache = *scx->pseccache;
 | 
			
		||||
	if (pseccache) {
 | 
			
		||||
		free(pseccache->head.first_securid);
 | 
			
		||||
#if CACHE_LEGACY_SIZE
 | 
			
		||||
                free((*scx->pseccache)->head.first_legacy);
 | 
			
		||||
                free(pseccache->head.first_legacy);
 | 
			
		||||
#endif
 | 
			
		||||
		free(*scx->pseccache);
 | 
			
		||||
		for (index1=0; index1<=pseccache->head.last; index1++)
 | 
			
		||||
			if (pseccache->cachetable[index1])
 | 
			
		||||
				free(pseccache->cachetable[index1]);
 | 
			
		||||
		free(pseccache);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1949,82 +1961,50 @@ static struct CACHED_PERMISSIONS *invalidate_legacy(struct SECURITY_CONTEXT *scx
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *	Resize permission cache in either direction
 | 
			
		||||
 *	Resize permission cache table
 | 
			
		||||
 *	do not call unless resizing is needed
 | 
			
		||||
 *	
 | 
			
		||||
 *	returns pointer to required entry or NULL if not possible
 | 
			
		||||
 *	If allocation fails, the cache size is not updated
 | 
			
		||||
 *	Lack of memory is not considered as an error, the cache is left
 | 
			
		||||
 *	consistent and errno is not set.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static struct CACHED_PERMISSIONS *resize_cache(
 | 
			
		||||
			struct SECURITY_CONTEXT *scx,
 | 
			
		||||
static void resize_cache(struct SECURITY_CONTEXT *scx,
 | 
			
		||||
			u32 securindex)
 | 
			
		||||
{
 | 
			
		||||
	struct CACHED_PERMISSIONS *cacheentry;
 | 
			
		||||
	struct SECURITY_CACHE *oldcache;
 | 
			
		||||
	struct SECURITY_CACHE *newcache;
 | 
			
		||||
	int oldcnt;
 | 
			
		||||
	int newcnt;
 | 
			
		||||
	BOOL beyond;
 | 
			
		||||
	int oldcnt;
 | 
			
		||||
	unsigned int index1;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 | 
			
		||||
	oldcache = *scx->pseccache;
 | 
			
		||||
	beyond = oldcache->head.last < securindex;
 | 
			
		||||
	if (beyond)
 | 
			
		||||
		newcnt = securindex - oldcache->head.first + 1;
 | 
			
		||||
	else
 | 
			
		||||
		newcnt = oldcache->head.last - securindex + 1;
 | 
			
		||||
	if (beyond && (newcnt <= CACHE_PERMISSIONS_SIZE)) {
 | 
			
		||||
		/* expand cache beyond current end */
 | 
			
		||||
#if 1
 | 
			
		||||
		newcache = (struct SECURITY_CACHE*)
 | 
			
		||||
			realloc(oldcache,
 | 
			
		||||
			    sizeof(struct SECURITY_CACHE)
 | 
			
		||||
			      + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS));
 | 
			
		||||
#else
 | 
			
		||||
		oldcnt = oldcache->head.last - oldcache->head.first + 1;
 | 
			
		||||
	index1 = securindex >> CACHE_PERMISSIONS_BITS;
 | 
			
		||||
	newcnt = index1 + 1;
 | 
			
		||||
	if (newcnt <= ((CACHE_PERMISSIONS_SIZE
 | 
			
		||||
			+ (1 << CACHE_PERMISSIONS_BITS)
 | 
			
		||||
			- 1) >> CACHE_PERMISSIONS_BITS)) {
 | 
			
		||||
		/* expand cache beyond current end, do not use realloc() */
 | 
			
		||||
		/* to avoid losing data when there is no more memory */
 | 
			
		||||
		oldcnt = oldcache->head.last + 1;
 | 
			
		||||
		newcache = (struct SECURITY_CACHE*)
 | 
			
		||||
			ntfs_malloc(
 | 
			
		||||
			    sizeof(struct SECURITY_CACHE)
 | 
			
		||||
			      + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS));
 | 
			
		||||
		memcpy(newcache,oldcache,
 | 
			
		||||
			      + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
 | 
			
		||||
		if (newcache) {
 | 
			
		||||
			memcpy(newcache,oldcache,
 | 
			
		||||
			    sizeof(struct SECURITY_CACHE)
 | 
			
		||||
			      + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS));
 | 
			
		||||
		free(oldcache);
 | 
			
		||||
#endif
 | 
			
		||||
		if (newcache) {
 | 
			
		||||
			     /* mark new entries as not valid */
 | 
			
		||||
			for (i=newcache->head.last+1; i<=securindex; i++)
 | 
			
		||||
				newcache->cachetable[
 | 
			
		||||
					i - newcache->head.first].valid = 0;
 | 
			
		||||
			newcache->head.last = securindex;
 | 
			
		||||
			*scx->pseccache = newcache;
 | 
			
		||||
			cacheentry = &newcache->
 | 
			
		||||
				cachetable[securindex - newcache->head.first];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!beyond && (newcnt <= CACHE_PERMISSIONS_SIZE)) {
 | 
			
		||||
		/* expand cache before current beginning */
 | 
			
		||||
		newcache = (struct SECURITY_CACHE*)
 | 
			
		||||
			ntfs_malloc(sizeof(struct SECURITY_CACHE)
 | 
			
		||||
			    +  (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS));
 | 
			
		||||
		if (newcache) {
 | 
			
		||||
			     /* mark new entries as not valid */
 | 
			
		||||
			for (i=securindex; i<oldcache->head.first; i++)
 | 
			
		||||
				newcache->cachetable[i - securindex].valid = 0;
 | 
			
		||||
			newcache->head = oldcache->head;
 | 
			
		||||
			newcache->head.first = securindex;
 | 
			
		||||
			oldcnt = oldcache->head.last - oldcache->head.first + 1;
 | 
			
		||||
			memcpy(&newcache->cachetable[oldcache->head.first
 | 
			
		||||
						 - newcache->head.first],
 | 
			
		||||
				oldcache->cachetable,
 | 
			
		||||
				oldcnt*sizeof(struct CACHED_PERMISSIONS));
 | 
			
		||||
			*scx->pseccache = newcache;
 | 
			
		||||
			      + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
 | 
			
		||||
			free(oldcache);
 | 
			
		||||
			cacheentry = &newcache->cachetable[0];
 | 
			
		||||
			     /* mark new entries as not valid */
 | 
			
		||||
			for (i=newcache->head.last+1; i<=index1; i++)
 | 
			
		||||
				newcache->cachetable[i]
 | 
			
		||||
					 = (struct CACHED_PERMISSIONS*)NULL;
 | 
			
		||||
			newcache->head.last = index1;
 | 
			
		||||
			*scx->pseccache = newcache;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return (cacheentry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -2039,8 +2019,12 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
		ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
 | 
			
		||||
{
 | 
			
		||||
	struct CACHED_PERMISSIONS *cacheentry;
 | 
			
		||||
	struct CACHED_PERMISSIONS *cacheblock;
 | 
			
		||||
	struct SECURITY_CACHE *pcache;
 | 
			
		||||
	u32 securindex;
 | 
			
		||||
	unsigned int index1;
 | 
			
		||||
	unsigned int index2;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* cacheing is only possible if a security_id has been defined */
 | 
			
		||||
	if (test_nino_flag(ni, v3_Extensions)
 | 
			
		||||
| 
						 | 
				
			
			@ -2050,12 +2034,13 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
		 *  where the entry exists
 | 
			
		||||
		 */
 | 
			
		||||
		securindex = le32_to_cpu(ni->security_id);
 | 
			
		||||
		index1 = securindex >> CACHE_PERMISSIONS_BITS;
 | 
			
		||||
		index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
 | 
			
		||||
		pcache = *scx->pseccache;
 | 
			
		||||
		if (pcache
 | 
			
		||||
		     && (pcache->head.first <= securindex)
 | 
			
		||||
		     && (pcache->head.last >= securindex)) {
 | 
			
		||||
			cacheentry = &pcache->cachetable[securindex
 | 
			
		||||
					 - pcache->head.first];
 | 
			
		||||
		     && (pcache->head.last >= index1)
 | 
			
		||||
		     && pcache->cachetable[index1]) {
 | 
			
		||||
			cacheentry = &pcache->cachetable[index1][index2];
 | 
			
		||||
			cacheentry->uid = uid;
 | 
			
		||||
			cacheentry->gid = gid;
 | 
			
		||||
			cacheentry->mode = mode & 07777;
 | 
			
		||||
| 
						 | 
				
			
			@ -2065,22 +2050,34 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
			pcache->head.p_writes++;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (!pcache) {
 | 
			
		||||
				/* create the first cache entry */
 | 
			
		||||
				/* create the first cache block */
 | 
			
		||||
				pcache = create_caches(scx, securindex);
 | 
			
		||||
				cacheentry = &pcache->cachetable[0];
 | 
			
		||||
			} else {
 | 
			
		||||
				cacheentry = resize_cache(scx, securindex);
 | 
			
		||||
				pcache = *scx->pseccache;
 | 
			
		||||
			}
 | 
			
		||||
			if (cacheentry) {
 | 
			
		||||
				cacheentry->uid = uid;
 | 
			
		||||
				cacheentry->gid = gid;
 | 
			
		||||
				cacheentry->mode = mode & 07777;
 | 
			
		||||
				cacheentry->inh_fileid = cpu_to_le32(0);
 | 
			
		||||
				cacheentry->inh_dirid = cpu_to_le32(0);
 | 
			
		||||
				cacheentry->valid = 1;
 | 
			
		||||
				pcache->head.p_writes++;
 | 
			
		||||
				if (index1 > pcache->head.last) {
 | 
			
		||||
					resize_cache(scx, securindex);
 | 
			
		||||
					pcache = *scx->pseccache;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			/* allocate block, if cache table was allocated */
 | 
			
		||||
			if (pcache && (index1 <= pcache->head.last)) {
 | 
			
		||||
				cacheblock = (struct CACHED_PERMISSIONS*)
 | 
			
		||||
					malloc(sizeof(struct CACHED_PERMISSIONS)
 | 
			
		||||
						<< CACHE_PERMISSIONS_BITS);
 | 
			
		||||
				pcache->cachetable[index1] = cacheblock;
 | 
			
		||||
				for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
 | 
			
		||||
					cacheblock[i].valid = 0;
 | 
			
		||||
				cacheentry = &cacheblock[index2];
 | 
			
		||||
				if (cacheentry) {
 | 
			
		||||
					cacheentry->uid = uid;
 | 
			
		||||
					cacheentry->gid = gid;
 | 
			
		||||
					cacheentry->mode = mode & 07777;
 | 
			
		||||
					cacheentry->inh_fileid = cpu_to_le32(0);
 | 
			
		||||
					cacheentry->inh_dirid = cpu_to_le32(0);
 | 
			
		||||
					cacheentry->valid = 1;
 | 
			
		||||
					pcache->head.p_writes++;
 | 
			
		||||
				}
 | 
			
		||||
			} else
 | 
			
		||||
				cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 | 
			
		||||
		}
 | 
			
		||||
	} else
 | 
			
		||||
#if CACHE_LEGACY_SIZE
 | 
			
		||||
| 
						 | 
				
			
			@ -2106,18 +2103,21 @@ static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
 | 
			
		|||
	struct CACHED_PERMISSIONS *cacheentry;
 | 
			
		||||
	struct SECURITY_CACHE *pcache;
 | 
			
		||||
	u32 securindex;
 | 
			
		||||
	unsigned int index1;
 | 
			
		||||
	unsigned int index2;
 | 
			
		||||
 | 
			
		||||
	/* cacheing is only possible if a security_id has been defined */
 | 
			
		||||
	cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 | 
			
		||||
	if (test_nino_flag(ni, v3_Extensions)
 | 
			
		||||
	   && (ni->security_id)) {
 | 
			
		||||
		securindex = le32_to_cpu(ni->security_id);
 | 
			
		||||
		index1 = securindex >> CACHE_PERMISSIONS_BITS;
 | 
			
		||||
		index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
 | 
			
		||||
		pcache = *scx->pseccache;
 | 
			
		||||
		if (pcache
 | 
			
		||||
		     && (pcache->head.first <= securindex)
 | 
			
		||||
		     && (pcache->head.last >= securindex)) {
 | 
			
		||||
			cacheentry = &pcache->cachetable[securindex
 | 
			
		||||
					 - pcache->head.first];
 | 
			
		||||
		     && (pcache->head.last >= index1)
 | 
			
		||||
		     && pcache->cachetable[index1]) {
 | 
			
		||||
			cacheentry = &pcache->cachetable[index1][index2];
 | 
			
		||||
			/* reject if entry is not valid */
 | 
			
		||||
			if (!cacheentry->valid)
 | 
			
		||||
				cacheentry = (struct CACHED_PERMISSIONS*)NULL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue