Implemented cache extension for legacy directories

N2009_11_14_FIXES
jpandre 2007-10-20 12:40:22 +00:00
parent 0ea556dee5
commit 0c15d089ef
2 changed files with 220 additions and 29 deletions

View File

@ -52,6 +52,16 @@ struct CACHED_PERMISSIONS {
unsigned int valid:1;
} ;
/*
* Entry in the permissions cache for directories with no security_id
*/
struct CACHED_PERMISSIONS_LEGACY {
struct CACHED_PERMISSIONS permissions;
struct CACHED_PERMISSIONS_LEGACY *next;
u64 mft_no;
} ;
/*
* Entry in the securid cache
*/
@ -73,6 +83,8 @@ struct SECURITY_HEAD {
unsigned int last;
struct CACHED_SECURID *first_securid;
struct CACHED_SECURID *most_recent_securid;
struct CACHED_PERMISSIONS_LEGACY *first_legacy;
struct CACHED_PERMISSIONS_LEGACY *most_recent_legacy;
/* statistics for permissions */
unsigned long p_writes;
unsigned long p_reads;

View File

@ -31,8 +31,9 @@
#define BUFSZ 1024 /* buffer size to read mapping file */
#define MAPPINGFILE "/NTFS-3G/UserMapping" /* name of mapping file */
#define LINESZ 120 /* maximum useful size of a mapping line */
#define CACHE_SECURID_SIZE 8 /* securid cache size >= 3 and not too big */
#define CACHE_SECURID_SIZE 16 /* securid cache, size >= 3 and not too big */
#define CACHE_PERMISSIONS_SIZE 4000 /* think twice before increasing */
#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */
#ifdef HAVE_CONFIG_H
#include "config.h"
@ -1500,7 +1501,7 @@ static const SID *find_gsid(struct SECURITY_CONTEXT *scx, gid_t gid)
* - from uid, gid and perm to securid (CACHED_SECURID)
* - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
*
* CACHED_SECURID data is kept in a most-recent-first lists
* CACHED_SECURID data is kept in a most-recent-first list
* which should not be too long to be efficient. Its optimal
* size is depends on usage and is hard to determine.
*
@ -1510,9 +1511,17 @@ static const SID *find_gsid(struct SECURITY_CONTEXT *scx, gid_t gid)
* standard usage, but not for file servers with too many file
* owners
*
* In both caches, data is never invalidated, however returned
* entries may be overwritten at next update, so data has
* to be copied elsewhere before another cache update is made.
* CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
* for legacy directories which were not allocated a security_id
* it is organized in a most-recent-first list.
*
* In main caches, data is never invalidated, as the meaning of
* a security_id only changes when user mapping is changed, which
* current implies remounting. However returned entries may be
* overwritten at next update, so data has to be copied elsewhere
* before another cache update is made.
* In legacy cache, data has to be invalidated when protection is
* changed.
*
* Though the same data may be found in both list, they
* must be kept separately : the interpretation of ACL
@ -1525,8 +1534,12 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
{
struct CACHED_SECURID *cachesecurid;
struct SECURITY_CACHE *cache;
#if CACHE_LEGACY_SIZE
struct CACHED_PERMISSIONS_LEGACY *cachelegacy;
#endif
int i;
cache = (struct SECURITY_CACHE*)NULL;
/* create the securid cache first */
cachesecurid = (struct CACHED_SECURID*)
ntfs_malloc(CACHE_SECURID_SIZE*sizeof(struct CACHED_SECURID));
@ -1540,27 +1553,48 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
cachesecurid[CACHE_SECURID_SIZE - 1].next =
(struct CACHED_SECURID*)NULL;
cachesecurid[CACHE_SECURID_SIZE - 1].mode = -1;
/* create the first permissions cache entry */
cache = (struct SECURITY_CACHE*)
ntfs_malloc(sizeof(struct SECURITY_CACHE));
if (cache) {
cache->head.first = securindex;
cache->head.last = securindex;
cache->head.p_reads = 0;
cache->head.p_hits = 0;
cache->head.p_writes = 0;
cache->head.s_reads = 0;
cache->head.s_hits = 0;
cache->head.s_writes = 0;
cache->head.s_hops = 0;
*scx->pseccache = cache;
cache->head.first_securid = cachesecurid;
cache->head.most_recent_securid = cachesecurid;
cache->cachetable[0].valid = 0;
#if CACHE_LEGACY_SIZE
/* create the legacy cache if needed */
cachelegacy = (struct CACHED_PERMISSIONS_LEGACY*)
ntfs_malloc(CACHE_LEGACY_SIZE*
sizeof(struct CACHED_PERMISSIONS_LEGACY));
if (cachelegacy) {
/* chain the entries, and mark an invalid entry */
for (i=0; i<(CACHE_LEGACY_SIZE - 1); i++) {
cachelegacy[i].next = &cachelegacy[i+1];
cachelegacy[i].permissions.valid = 0;
}
/* special for the last entry */
cachelegacy[CACHE_LEGACY_SIZE - 1].next =
(struct CACHED_PERMISSIONS_LEGACY*)NULL;
cachelegacy[CACHE_LEGACY_SIZE - 1].permissions.valid = 0;;
#endif
/* create the first permissions cache entry */
cache = (struct SECURITY_CACHE*)
ntfs_malloc(sizeof(struct SECURITY_CACHE));
if (cache) {
cache->head.first = securindex;
cache->head.last = securindex;
cache->head.p_reads = 0;
cache->head.p_hits = 0;
cache->head.p_writes = 0;
cache->head.s_reads = 0;
cache->head.s_hits = 0;
cache->head.s_writes = 0;
cache->head.s_hops = 0;
*scx->pseccache = cache;
cache->head.first_securid = cachesecurid;
cache->head.most_recent_securid = cachesecurid;
cache->cachetable[0].valid = 0;
#if CACHE_LEGACY_SIZE
cache->head.first_legacy = cachelegacy;
cache->head.most_recent_legacy = cachelegacy;
#endif
}
#if CACHE_LEGACY_SIZE
}
} else
cache = (struct SECURITY_CACHE*)NULL;
#endif
}
return (cache);
}
@ -1573,6 +1607,9 @@ static void free_caches(struct SECURITY_CONTEXT *scx)
{
if (*scx->pseccache) {
free((*scx->pseccache)->head.first_securid);
#if CACHE_LEGACY_SIZE
free((*scx->pseccache)->head.first_legacy);
#endif
free(*scx->pseccache);
}
}
@ -1615,9 +1652,9 @@ static const struct CACHED_SECURID *fetch_securid(struct SECURITY_CONTEXT *scx,
current->next = cache->head.most_recent_securid;
cache->head.most_recent_securid = current;
}
cache->head.s_reads++;
} else /* cache not ready */
current = (struct CACHED_SECURID*)NULL;
cache->head.s_reads++;
return (current);
}
@ -1673,13 +1710,144 @@ static const struct CACHED_SECURID *enter_securid(struct SECURITY_CONTEXT *scx,
current->mode = mode;
current->securid = securid;
}
cache->head.s_writes++;
} else /* cache not available */
current = (struct CACHED_SECURID*)NULL;
cache->head.s_writes++;
return (current);
}
#if CACHE_LEGACY_SIZE
/*
* Fetch a legacy directory from cache
* returns the cache entry, or NULL if not available
*/
static struct CACHED_PERMISSIONS *fetch_legacy(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni)
{
struct SECURITY_CACHE *cache;
struct CACHED_PERMISSIONS_LEGACY *current;
struct CACHED_PERMISSIONS_LEGACY *previous;
struct CACHED_PERMISSIONS *cacheentry;
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
cache = *scx->pseccache;
if (cache
&& (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
/*
* Search sequentially in LRU list
*/
current = cache->head.most_recent_legacy;
previous = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
while (current
&& (!current->permissions.valid
|| (current->mft_no != ni->mft_no))) {
previous = current;
current = current->next;
}
if (current) {
cache->head.p_hits++;
cacheentry = &current->permissions;
}
if (current && previous) {
/*
* found and not at head of list, unlink from current
* position and relink as head of list
*/
previous->next = current->next;
current->next = cache->head.most_recent_legacy;
cache->head.most_recent_legacy = current;
}
cache->head.p_reads++;
}
return (cacheentry);
}
/*
* Enter a legacy directory into cache
* returns the cache entry
*/
static struct CACHED_PERMISSIONS *enter_legacy(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni, uid_t uid, gid_t gid,
mode_t mode)
{
struct SECURITY_CACHE *cache;
struct CACHED_PERMISSIONS_LEGACY *current;
struct CACHED_PERMISSIONS_LEGACY *previous;
struct CACHED_PERMISSIONS_LEGACY *before;
struct CACHED_PERMISSIONS *cacheentry;
mode &= 07777;
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
cache = *scx->pseccache;
if ((cache || (cache = create_caches(scx, FIRST_SECURITY_ID)))
&& (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
/*
* Search sequentially in LRU list to locate the end,
* and find out whether the entry is already in list
* As we normally go to the end, no statitics is
* kept.
*/
current = cache->head.most_recent_legacy;
previous = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
before = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
while (current
&& (!current->permissions.valid
|| (current->mft_no != ni->mft_no))) {
before = previous;
previous = current;
current = current->next;
}
if (!current) {
/*
* Not in list, reuse the last entry,
* and relink as head of list
* Note : we assume at least three entries, so
* before, previous and first are always different
*/
before->next = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
previous->next = cache->head.most_recent_legacy;
cache->head.most_recent_legacy = previous;
current = previous;
current->mft_no = ni->mft_no;
cacheentry = &current->permissions;
cacheentry->uid = uid;
cacheentry->gid = gid;
cacheentry->mode = mode;
cacheentry->inh_fileid = cpu_to_le32(0);
cacheentry->inh_dirid = cpu_to_le32(0);
cacheentry->valid = 1;
}
cache->head.p_writes++;
}
return (cacheentry);
}
/*
* Invalidate a legacy directory entry in cache
* This is needed after a change in directory protection, when
* no security id has been assigned (only for NTFS 1.x)
*
* returns the cache entry, or NULL if not available
*/
static struct CACHED_PERMISSIONS *invalidate_legacy(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni)
{
struct CACHED_PERMISSIONS *cached;
cached = fetch_legacy(scx, ni);
if (cached) cached->valid = 0;
return (cached);
}
#endif
/*
* Resize permission cache in either direction
* do not call unless resizing is needed
@ -1815,7 +1983,11 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
}
}
} else
#if CACHE_LEGACY_SIZE
cacheentry = enter_legacy(scx, ni, uid, gid, mode);
#else
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
#endif
return (cacheentry);
}
@ -1855,6 +2027,10 @@ static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
pcache->head.p_reads++;
}
}
#if CACHE_LEGACY_SIZE
else
cacheentry = fetch_legacy(scx, ni);
#endif
return (cacheentry);
}
@ -2928,6 +3104,9 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
enter_securid(scx, uid,
gid, mode,
ni->security_id);
/* also invalidate legacy cache */
if (isdir && !ni->security_id)
invalidate_legacy(scx, ni);
}
free(newattr);
} else {
@ -3359,7 +3538,7 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
newattrsz = parentattrsz;
if (usid) newattrsz += sid_size(usid);
if (gsid) newattrsz += sid_size(gsid);
newattr = (char*)malloc(parentattrsz);
newattr = (char*)ntfs_malloc(parentattrsz);
if (newattr) {
pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
@ -4119,7 +4298,7 @@ static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
oldattrsz = attr_size(oldattr);
newattrsz = attr_size(newattr);
target = (char*)malloc(oldattrsz + newattrsz);
target = (char*)ntfs_malloc(oldattrsz + newattrsz);
if (target) {
targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);