Reengineered permissions cache

N2009_11_14_FIXES
jpandre 2007-12-13 09:14:51 +00:00
parent 7335c9af68
commit 59a21e6110
2 changed files with 91 additions and 92 deletions

View File

@ -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 */
} ;
/*

View File

@ -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;