#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "types.h"
#include "security.h"
#include "cache.h"
#include "misc.h"
#include "logging.h"
static void inserthashindex(struct CACHE_HEADER *cache,
struct CACHED_GENERIC *current)
{
int h;
struct HASH_ENTRY *link;
struct HASH_ENTRY *first;
if (cache->dohash) {
h = cache->dohash(current);
if ((h >= 0) && (h < cache->max_hash)) {
link = cache->free_hash;
if (link) {
cache->free_hash = link->next;
first = cache->first_hash[h];
if (first)
link->next = first;
else
link->next = NULL;
link->entry = current;
cache->first_hash[h] = link;
} else {
ntfs_log_error("No more hash entries,"
" cache %s hashing dropped\n",
cache->name);
cache->dohash = (cache_hash)NULL;
}
} else {
ntfs_log_error("Illegal hash value,"
" cache %s hashing dropped\n",
cache->name);
cache->dohash = (cache_hash)NULL;
}
}
}
static void drophashindex(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *current, int hash)
{
struct HASH_ENTRY *link;
struct HASH_ENTRY *previous;
if (cache->dohash) {
if ((hash >= 0) && (hash < cache->max_hash)) {
link = cache->first_hash[hash];
previous = (struct HASH_ENTRY*)NULL;
while (link && (link->entry != current)) {
previous = link;
link = link->next;
}
if (link) {
if (previous)
previous->next = link->next;
else
cache->first_hash[hash] = link->next;
link->next = cache->free_hash;
cache->free_hash = link;
} else {
ntfs_log_error("Bad hash list,"
" cache %s hashing dropped\n",
cache->name);
cache->dohash = (cache_hash)NULL;
}
} else {
ntfs_log_error("Illegal hash value,"
" cache %s hashing dropped\n",
cache->name);
cache->dohash = (cache_hash)NULL;
}
}
}
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *wanted, cache_compare compare)
{
struct CACHED_GENERIC *current;
struct CACHED_GENERIC *previous;
struct HASH_ENTRY *link;
int h;
current = (struct CACHED_GENERIC*)NULL;
if (cache) {
if (cache->dohash) {
h = cache->dohash(wanted);
link = cache->first_hash[h];
while (link && compare(link->entry, wanted))
link = link->next;
if (link)
current = link->entry;
}
if (!cache->dohash) {
current = cache->most_recent_entry;
while (current
&& compare(current, wanted)) {
current = current->next;
}
}
if (current) {
previous = current->previous;
cache->hits++;
if (previous) {
previous->next = current->next;
if (current->next)
current->next->previous
= current->previous;
else
cache->oldest_entry
= current->previous;
current->next = cache->most_recent_entry;
current->previous
= (struct CACHED_GENERIC*)NULL;
cache->most_recent_entry->previous = current;
cache->most_recent_entry = current;
}
}
cache->reads++;
}
return (current);
}
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *item,
cache_compare compare)
{
struct CACHED_GENERIC *current;
struct CACHED_GENERIC *before;
struct HASH_ENTRY *link;
int h;
current = (struct CACHED_GENERIC*)NULL;
if (cache) {
if (cache->dohash) {
h = cache->dohash(item);
link = cache->first_hash[h];
while (link && compare(link->entry, item))
link = link->next;
if (link) {
current = link->entry;
}
}
if (!cache->dohash) {
current = cache->most_recent_entry;
while (current
&& compare(current, item)) {
current = current->next;
}
}
if (!current) {
if (cache->free_entry) {
current = cache->free_entry;
cache->free_entry = cache->free_entry->next;
if (item->varsize) {
current->variable = ntfs_malloc(
item->varsize);
} else
current->variable = (void*)NULL;
current->varsize = item->varsize;
if (!cache->oldest_entry)
cache->oldest_entry = current;
} else {
current = cache->oldest_entry;
before = current->previous;
before->next = (struct CACHED_GENERIC*)NULL;
if (cache->dohash)
drophashindex(cache,current,
cache->dohash(current));
if (cache->dofree)
cache->dofree(current);
cache->oldest_entry = current->previous;
if (item->varsize) {
if (current->varsize)
current->variable = realloc(
current->variable,
item->varsize);
else
current->variable = ntfs_malloc(
item->varsize);
} else {
if (current->varsize)
free(current->variable);
current->variable = (void*)NULL;
}
current->varsize = item->varsize;
}
current->next = cache->most_recent_entry;
current->previous = (struct CACHED_GENERIC*)NULL;
if (cache->most_recent_entry)
cache->most_recent_entry->previous = current;
cache->most_recent_entry = current;
memcpy(current->payload, item->payload, cache->fixed_size);
if (item->varsize) {
if (current->variable) {
memcpy(current->variable,
item->variable, item->varsize);
} else {
cache->most_recent_entry = current->next;
current->next = cache->free_entry;
cache->free_entry = current;
current = (struct CACHED_GENERIC*)NULL;
}
} else {
current->variable = (void*)NULL;
current->varsize = 0;
}
if (cache->dohash && current)
inserthashindex(cache,current);
}
cache->writes++;
}
return (current);
}
static void do_invalidate(struct CACHE_HEADER *cache,
struct CACHED_GENERIC *current, int flags)
{
struct CACHED_GENERIC *previous;
previous = current->previous;
if ((flags & CACHE_FREE) && cache->dofree)
cache->dofree(current);
if (current->next)
current->next->previous = current->previous;
else
cache->oldest_entry = current->previous;
if (previous)
previous->next = current->next;
else
cache->most_recent_entry = current->next;
current->next = cache->free_entry;
cache->free_entry = current;
if (current->variable)
free(current->variable);
current->varsize = 0;
}
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *item, cache_compare compare,
int flags)
{
struct CACHED_GENERIC *current;
struct CACHED_GENERIC *next;
struct HASH_ENTRY *link;
int count;
int h;
current = (struct CACHED_GENERIC*)NULL;
count = 0;
if (cache) {
if (!(flags & CACHE_NOHASH) && cache->dohash) {
h = cache->dohash(item);
link = cache->first_hash[h];
while (link) {
if (compare(link->entry, item))
link = link->next;
else {
current = link->entry;
link = link->next;
if (current) {
drophashindex(cache,current,h);
do_invalidate(cache,
current,flags);
count++;
}
}
}
}
if ((flags & CACHE_NOHASH) || !cache->dohash) {
current = cache->most_recent_entry;
while (current) {
if (!compare(current, item)) {
next = current->next;
if (cache->dohash)
drophashindex(cache,current,
cache->dohash(current));
do_invalidate(cache,current,flags);
current = next;
count++;
} else {
current = current->next;
}
}
}
}
return (count);
}
int ntfs_remove_cache(struct CACHE_HEADER *cache,
struct CACHED_GENERIC *item, int flags)
{
int count;
count = 0;
if (cache) {
if (cache->dohash)
drophashindex(cache,item,cache->dohash(item));
do_invalidate(cache,item,flags);
count++;
}
return (count);
}
static void ntfs_free_cache(struct CACHE_HEADER *cache)
{
struct CACHED_GENERIC *entry;
if (cache) {
for (entry=cache->most_recent_entry; entry; entry=entry->next) {
if (cache->dofree)
cache->dofree(entry);
if (entry->variable)
free(entry->variable);
}
free(cache);
}
}
static struct CACHE_HEADER *ntfs_create_cache(const char *name,
cache_free dofree, cache_hash dohash,
int full_item_size,
int item_count, int max_hash)
{
struct CACHE_HEADER *cache;
struct CACHED_GENERIC *pc;
struct CACHED_GENERIC *qc;
struct HASH_ENTRY *ph;
struct HASH_ENTRY *qh;
struct HASH_ENTRY **px;
size_t size;
int i;
size = sizeof(struct CACHE_HEADER) + item_count*full_item_size;
if (max_hash)
size += item_count*sizeof(struct HASH_ENTRY)
+ max_hash*sizeof(struct HASH_ENTRY*);
cache = (struct CACHE_HEADER*)ntfs_malloc(size);
if (cache) {
cache->name = name;
cache->dofree = dofree;
if (dohash && max_hash) {
cache->dohash = dohash;
cache->max_hash = max_hash;
} else {
cache->dohash = (cache_hash)NULL;
cache->max_hash = 0;
}
cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC);
cache->reads = 0;
cache->writes = 0;
cache->hits = 0;
cache->most_recent_entry = (struct CACHED_GENERIC*)NULL;
cache->oldest_entry = (struct CACHED_GENERIC*)NULL;
cache->free_entry = &cache->entry[0];
pc = &cache->entry[0];
for (i=0; i<(item_count - 1); i++) {
qc = (struct CACHED_GENERIC*)((char*)pc
+ full_item_size);
pc->next = qc;
pc->variable = (void*)NULL;
pc->varsize = 0;
pc = qc;
}
pc->next = (struct CACHED_GENERIC*)NULL;
pc->variable = (void*)NULL;
pc->varsize = 0;
if (max_hash) {
ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size);
cache->free_hash = ph;
for (i=0; i<(item_count - 1); i++) {
qh = &ph[1];
ph->next = qh;
ph = qh;
}
if (item_count) {
ph->next = (struct HASH_ENTRY*)NULL;
}
px = (struct HASH_ENTRY**)&ph[1];
cache->first_hash = px;
for (i=0; i<max_hash; i++)
px[i] = (struct HASH_ENTRY*)NULL;
} else {
cache->free_hash = (struct HASH_ENTRY*)NULL;
cache->first_hash = (struct HASH_ENTRY**)NULL;
}
}
return (cache);
}
void ntfs_create_lru_caches(ntfs_volume *vol)
{
#if CACHE_INODE_SIZE
vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL,
ntfs_dir_inode_hash, sizeof(struct CACHED_INODE),
CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE);
#endif
#if CACHE_NIDATA_SIZE
vol->nidata_cache = ntfs_create_cache("nidata",
ntfs_inode_nidata_free, ntfs_inode_nidata_hash,
sizeof(struct CACHED_NIDATA),
CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE);
#endif
#if CACHE_LOOKUP_SIZE
vol->lookup_cache = ntfs_create_cache("lookup",
(cache_free)NULL, ntfs_dir_lookup_hash,
sizeof(struct CACHED_LOOKUP),
CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE);
#endif
vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL,
(cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0);
#if CACHE_LEGACY_SIZE
vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL,
(cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0);
#endif
}
void ntfs_free_lru_caches(ntfs_volume *vol)
{
#if CACHE_INODE_SIZE
ntfs_free_cache(vol->xinode_cache);
#endif
#if CACHE_NIDATA_SIZE
ntfs_free_cache(vol->nidata_cache);
#endif
#if CACHE_LOOKUP_SIZE
ntfs_free_cache(vol->lookup_cache);
#endif
ntfs_free_cache(vol->securid_cache);
#if CACHE_LEGACY_SIZE
ntfs_free_cache(vol->legacy_cache);
#endif
}