Redesigned caches for indexing cached entries

PERMISSION_HANDLING_BRANCH
jpandre 2009-12-17 17:36:06 +00:00
parent 7c88ccb95b
commit 41a371f4a7
11 changed files with 785 additions and 377 deletions

View File

@ -7,6 +7,7 @@ headers = \
attrlist.h \
bitmap.h \
bootsect.h \
cache.h \
collate.h \
compat.h \
compress.h \
@ -27,7 +28,7 @@ headers = \
mst.h \
ntfstime.h \
object_id.h \
param.h \
param.h \
reparse.h \
runlist.h \
security.h \

View File

@ -0,0 +1,115 @@
/*
* cache.h : deal with indexed LRU caches
*
* Copyright (c) 2008-2009 Jean-Pierre Andre
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the NTFS-3G
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NTFS_CACHE_H_
#define _NTFS_CACHE_H_
#include "volume.h"
struct CACHED_GENERIC {
struct CACHED_GENERIC *next;
struct CACHED_GENERIC *previous;
void *variable;
size_t varsize;
void *fixed[0];
} ;
struct CACHED_INODE {
struct CACHED_INODE *next;
struct CACHED_INODE *previous;
const char *pathname;
size_t varsize;
/* above fields must match "struct CACHED_GENERIC" */
u64 inum;
} ;
struct CACHED_NIDATA {
struct CACHED_NIDATA *next;
struct CACHED_NIDATA *previous;
const char *pathname; /* not used */
size_t varsize; /* not used */
/* above fields must match "struct CACHED_GENERIC" */
u64 inum;
ntfs_inode *ni;
} ;
struct CACHED_LOOKUP {
struct CACHED_LOOKUP *next;
struct CACHED_LOOKUP *previous;
const char *name;
size_t namesize;
/* above fields must match "struct CACHED_GENERIC" */
u64 parent;
u64 inum;
} ;
enum {
CACHE_FREE = 1,
CACHE_NOHASH = 2
} ;
typedef int (*cache_compare)(const struct CACHED_GENERIC *cached,
const struct CACHED_GENERIC *item);
typedef void (*cache_free)(const struct CACHED_GENERIC *cached);
typedef int (*cache_hash)(const struct CACHED_GENERIC *cached);
struct HASH_ENTRY {
struct HASH_ENTRY *next;
struct CACHED_GENERIC *entry;
} ;
struct CACHE_HEADER {
const char *name;
struct CACHED_GENERIC *most_recent_entry;
struct CACHED_GENERIC *oldest_entry;
struct CACHED_GENERIC *free_entry;
struct HASH_ENTRY *free_hash;
struct HASH_ENTRY **first_hash;
cache_free dofree;
cache_hash dohash;
unsigned long reads;
unsigned long writes;
unsigned long hits;
int fixed_size;
int max_hash;
struct CACHED_GENERIC entry[0];
} ;
/* cast to generic, avoiding gcc warnings */
#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr))
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *wanted,
cache_compare compare);
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *item,
cache_compare compare);
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *item,
cache_compare compare, int flags);
int ntfs_remove_cache(struct CACHE_HEADER *cache,
struct CACHED_GENERIC *item, int flags);
void ntfs_create_lru_caches(ntfs_volume *vol);
void ntfs_free_lru_caches(ntfs_volume *vol);
#endif /* _NTFS_CACHE_H_ */

View File

@ -112,5 +112,14 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
const char *value, size_t size, int flags);
int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni);
#if CACHE_INODE_SIZE
struct CACHED_GENERIC;
extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached);
extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached);
#endif
#endif /* defined _NTFS_DIR_H */

View File

@ -1,7 +1,6 @@
/*
* misc.h : miscellaneous exports
* - memory allocation
* - LRU caches
*
* Copyright (c) 2008 Jean-Pierre Andre
*
@ -24,49 +23,6 @@
#ifndef _NTFS_MISC_H_
#define _NTFS_MISC_H_
#include "volume.h"
struct CACHED_GENERIC {
struct CACHED_GENERIC *next;
void *variable;
size_t varsize;
void *fixed[0];
} ;
struct CACHED_INODE {
struct CACHED_INODE *next;
const char *pathname;
size_t varsize;
/* above fields must match "struct CACHED_GENERIC" */
u64 inum;
} ;
typedef int (*cache_compare)(const struct CACHED_GENERIC *cached,
const struct CACHED_GENERIC *item);
struct CACHE_HEADER {
const char *name;
struct CACHED_GENERIC *most_recent_entry;
struct CACHED_GENERIC *free_entry;
unsigned long reads;
unsigned long writes;
unsigned long hits;
int fixed_size;
struct CACHED_GENERIC entry[0];
} ;
/* cast to generic, avoiding gcc warnings */
#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr))
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *wanted, cache_compare compare);
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *item, cache_compare compare);
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *item, cache_compare compare);
void ntfs_create_lru_caches(ntfs_volume *vol);
void ntfs_free_lru_caches(ntfs_volume *vol);
void *ntfs_calloc(size_t size);
void *ntfs_malloc(size_t size);

View File

@ -79,6 +79,7 @@ struct CACHED_PERMISSIONS {
struct CACHED_PERMISSIONS_LEGACY {
struct CACHED_PERMISSIONS_LEGACY *next;
struct CACHED_PERMISSIONS_LEGACY *previous;
void *variable;
size_t varsize;
/* above fields must match "struct CACHED_GENERIC" */
@ -92,6 +93,7 @@ struct CACHED_PERMISSIONS_LEGACY {
struct CACHED_SECURID {
struct CACHED_SECURID *next;
struct CACHED_SECURID *previous;
void *variable;
size_t varsize;
/* above fields must match "struct CACHED_GENERIC" */

View File

@ -23,6 +23,7 @@ libntfs_3g_la_SOURCES = \
attrlist.c \
bitmap.c \
bootsect.c \
cache.c \
collate.c \
compat.c \
compress.c \

589
libntfs-3g/cache.c 100644
View File

@ -0,0 +1,589 @@
/**
* cache.c : deal with LRU caches
*
* Copyright (c) 2008-2009 Jean-Pierre Andre
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the NTFS-3G
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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"
/*
* General functions to deal with LRU caches
*
* The cached data have to be organized in a structure in which
* the first fields must follow a mandatory pattern and further
* fields may contain any fixed size data. They are stored in an
* LRU list.
*
* A compare function must be provided for finding a wanted entry
* in the cache. Another function may be provided for invalidating
* an entry to facilitate multiple invalidation.
*
* These functions never return error codes. When there is a
* shortage of memory, data is simply not cached.
* When there is a hashing bug, hashing is dropped, and sequential
* searches are used.
*/
/*
* Enter a new hash index, after a new record has been inserted
*
* Do not call when a record has been modified (with no key change)
*/
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)) {
/* get a free link and insert at top of hash list */
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;
}
}
}
/*
* Drop a hash index when a record is about to be deleted
*/
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)) {
/* find the link and unlink */
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;
}
}
}
/*
* Fetch an entry from cache
*
* returns the cache entry, or NULL if not available
* The returned entry may be modified, but not freed
*/
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) {
/*
* When possible, use the hash table to
* locate the entry if present
*/
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) {
/*
* Search sequentially in LRU list if no hash table
* or if hashing has just failed
*/
current = cache->most_recent_entry;
while (current
&& compare(current, wanted)) {
current = current->next;
}
}
if (current) {
previous = current->previous;
cache->hits++;
if (previous) {
/*
* found and not at head of list, unlink from current
* position and relink as head of list
*/
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);
}
/*
* Enter an inode number into cache
* returns the cache entry or NULL if not possible
*/
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) {
/*
* When possible, use the hash table to
* find out whether the entry if present
*/
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) {
/*
* 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 statistics is
* kept.
*/
current = cache->most_recent_entry;
while (current
&& compare(current, item)) {
current = current->next;
}
}
if (!current) {
/*
* Not in list, get a free entry or reuse the
* last entry, and relink as head of list
* Note : we assume at least three entries, so
* before, previous and first are different when
* an entry is reused.
*/
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 {
/* reusing the oldest entry */
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->fixed, item->fixed, cache->fixed_size);
if (item->varsize) {
if (current->variable) {
memcpy(current->variable,
item->variable, item->varsize);
} else {
/*
* no more memory for variable part
* recycle entry in free list
* not an error, just uncacheable
*/
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);
}
/*
* Invalidate a cache entry
* The entry is moved to the free entry list
* A specific function may be called for entry deletion
*/
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);
/*
* Relink into free list
*/
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;
}
/*
* Invalidate entries in cache
*
* Several entries may have to be invalidated (at least for inodes
* associated to directories which have been renamed), a different
* compare function may be provided to select entries to invalidate
*
* Returns the number of deleted entries, this can be used by
* the caller to signal a cache corruption if the entry was
* supposed to be found.
*/
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 *previous;
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) {
/*
* When possible, use the hash table to
* find out whether the entry if present
*/
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) {
/*
* Search sequentially in LRU list
*/
current = cache->most_recent_entry;
previous = (struct CACHED_GENERIC*)NULL;
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 {
previous = current;
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);
}
/*
* Free memory allocated to a cache
*/
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);
}
}
/*
* Create a cache
*
* Returns the cache header, or NULL if the cache could not be created
*/
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) {
/* header */
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;
/* chain the data entries, and mark an invalid entry */
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;
}
/* special for the last entry */
pc->next = (struct CACHED_GENERIC*)NULL;
pc->variable = (void*)NULL;
pc->varsize = 0;
if (max_hash) {
/* chain the hash entries */
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;
}
/* special for the last entry */
if (item_count) {
ph->next = (struct HASH_ENTRY*)NULL;
}
/* create and initialize the hash indexes */
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);
}
/*
* Create all LRU caches
*
* No error return, if creation is not possible, cacheing will
* just be not available
*/
void ntfs_create_lru_caches(ntfs_volume *vol)
{
#if CACHE_INODE_SIZE
/* inode cache */
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
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
}
/*
* Free all LRU caches
*/
void ntfs_free_lru_caches(ntfs_volume *vol)
{
#if CACHE_INODE_SIZE
ntfs_free_cache(vol->xinode_cache);
#endif
ntfs_free_cache(vol->securid_cache);
#if CACHE_LEGACY_SIZE
ntfs_free_cache(vol->legacy_cache);
#endif
}

View File

@ -56,6 +56,7 @@
#include "ntfstime.h"
#include "lcnalloc.h"
#include "logging.h"
#include "cache.h"
#include "misc.h"
#include "security.h"
#include "reparse.h"
@ -87,6 +88,29 @@ ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'),
#if CACHE_INODE_SIZE
/*
* Pathname hashing
*
* Based on first char and second char (which may be '\0')
*/
int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached)
{
const char *path;
const unsigned char *name;
path = (const char*)cached->variable;
if (!path) {
ntfs_log_error("Bad inode cache entry\n");
return (-1);
}
name = (const unsigned char*)strrchr(path,'/');
if (!name)
name = (const unsigned char*)path;
return (((name[0] << 1) + name[1] + strlen((const char*)name))
% (2*CACHE_INODE_SIZE));
}
/*
* Pathname comparing for entering/fetching from cache
*/
@ -105,21 +129,31 @@ static int inode_cache_compare(const struct CACHED_GENERIC *cached,
* related to a renamed directory
* inode numbers are also checked, as deleting a long name may
* imply deleting a short name and conversely
*
* Only use associated with a CACHE_NOHASH flag
*/
static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached,
const struct CACHED_GENERIC *wanted)
{
int len;
BOOL different;
const struct CACHED_INODE *w;
const struct CACHED_INODE *c;
len = strlen(wanted->variable);
return (!cached->variable
|| ((((const struct CACHED_INODE*)wanted)->inum
!= MREF(((const struct CACHED_INODE*)cached)->inum))
&& (strncmp((const char*)cached->variable,
(const char*)wanted->variable,len)
|| ((((const char*)cached->variable)[len] != '\0')
&& (((const char*)cached->variable)[len] != '/')))));
w = (const struct CACHED_INODE*)wanted;
c = (const struct CACHED_INODE*)cached;
if (w->pathname) {
len = strlen(w->pathname);
different = !cached->variable
|| ((w->inum != MREF(c->inum))
&& (strncmp(c->pathname, w->pathname, len)
|| ((c->pathname[len] != '\0')
&& (c->pathname[len] != '/'))));
} else
different = !c->pathname
|| (w->inum != MREF(c->inum));
return (different);
}
#endif
@ -1628,6 +1662,26 @@ search:
*/
#if CACHE_INODE_SIZE
inum = ni->mft_no;
if (pathname) {
/* invalide cache entry, even if there was an error */
/* Remove leading /'s. */
p = pathname;
while (*p == PATH_SEP)
p++;
if (p[0] && (p[strlen(p)-1] == PATH_SEP))
ntfs_log_error("Unnormalized path %s\n",pathname);
item.pathname = p;
item.varsize = strlen(p);
} else {
item.pathname = (const char*)NULL;
item.varsize = 0;
}
item.inum = inum;
count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item),
inode_cache_inv_compare, CACHE_NOHASH);
if (pathname && !count)
ntfs_log_error("Could not delete inode cache entry for %s\n",
pathname);
#endif
if (ni->mrec->link_count) {
ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME);
@ -1698,24 +1752,6 @@ out:
err = errno;
if (ntfs_inode_close(ni) && !err)
err = errno;
#if CACHE_INODE_SIZE
if (pathname) {
/* invalide cache entry, even if there was an error */
/* Remove leading /'s. */
p = pathname;
while (*p == PATH_SEP)
p++;
if (p[0] && (p[strlen(p)-1] == PATH_SEP))
ntfs_log_error("Unnormalized path %s\n",pathname);
item.pathname = p;
item.inum = inum;
count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item),
inode_cache_inv_compare);
if (!count)
ntfs_log_error("Could not delete inode cache entry for %s\n",
pathname);
}
#endif
if (err) {
errno = err;
ntfs_log_debug("Could not delete file: %s\n", strerror(errno));

View File

@ -1,7 +1,6 @@
/**
* misc.c : miscellaneous :
* - dealing with errors in memory allocation
* - data caching
*
* Copyright (c) 2008 Jean-Pierre Andre
*
@ -33,7 +32,6 @@
#endif
#include "types.h"
#include "security.h"
#include "misc.h"
#include "logging.h"
@ -61,304 +59,3 @@ void *ntfs_malloc(size_t size)
ntfs_log_perror("Failed to malloc %lld bytes", (long long)size);
return p;
}
/*
* General functions to deal with LRU caches
*
* The cached data have to be organized in a structure in which
* the first fields must follow a mandatory pattern and further
* fields may contain any fixed size data. They are stored in an
* LRU list.
*
* A compare function must be provided for finding a wanted entry
* in the cache. Another function may be provided for invalidating
* an entry to facilitate multiple invalidation.
*
* These functions never return error codes. When there is a
* shortage of memory, data is simply not cached.
*/
/*
* Fetch an entry from cache
*
* returns the cache entry, or NULL if not available
*/
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;
current = (struct CACHED_GENERIC*)NULL;
if (cache) {
/*
* Search sequentially in LRU list
*/
current = cache->most_recent_entry;
previous = (struct CACHED_GENERIC*)NULL;
while (current
&& compare(current, wanted)) {
previous = current;
current = current->next;
}
if (current)
cache->hits++;
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->most_recent_entry;
cache->most_recent_entry = current;
}
cache->reads++;
}
return (current);
}
/*
* Enter an inode number into cache
* returns the cache entry or NULL if not possible
*/
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 *previous;
struct CACHED_GENERIC *before;
current = (struct CACHED_GENERIC*)NULL;
if (cache) {
/*
* 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 statistics is
* kept.
*/
current = cache->most_recent_entry;
previous = (struct CACHED_GENERIC*)NULL;
before = (struct CACHED_GENERIC*)NULL;
while (current
&& compare(current, item)) {
before = previous;
previous = current;
current = current->next;
}
if (!current) {
/*
* Not in list, get a free entry or reuse the
* last entry, and relink as head of list
* Note : we assume at least three entries, so
* before, previous and first are different when
* an entry is reused.
*/
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;
} else {
before->next = (struct CACHED_GENERIC*)NULL;
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;
cache->most_recent_entry = current;
memcpy(current->fixed, item->fixed, cache->fixed_size);
if (item->varsize) {
if (current->variable) {
memcpy(current->variable,
item->variable, item->varsize);
} else {
/*
* no more memory for variable part
* recycle entry in free list
* not an error, just uncacheable
*/
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;
}
}
cache->writes++;
}
return (current);
}
/*
* Invalidate entries in cache
*
* Several entries may have to be invalidated (at least for inodes
* associated to directories which have been renamed), a different
* compare function may be provided to select entries to invalidate
*
* Returns the number of deleted entries, this can be used by
* the caller to signal a cache corruption if the entry was
* supposed to be found.
*/
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
const struct CACHED_GENERIC *item, cache_compare compare)
{
struct CACHED_GENERIC *current;
struct CACHED_GENERIC *previous;
int count;
current = (struct CACHED_GENERIC*)NULL;
count = 0;
if (cache) {
/*
* Search sequentially in LRU list
*/
current = cache->most_recent_entry;
previous = (struct CACHED_GENERIC*)NULL;
while (current) {
if (!compare(current, item)) {
/*
* Relink into free list
*/
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;
if (previous)
current = previous->next;
else
current = cache->most_recent_entry;
count++;
} else {
previous = current;
current = current->next;
}
}
}
return (count);
}
/*
* Free memory allocated to a cache
*/
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 (entry->variable)
free(entry->variable);
free(cache);
}
}
/*
* Create a cache
*
* Returns the cache header, or NULL if the cache could not be created
*/
static struct CACHE_HEADER *ntfs_create_cache(const char *name,
int full_item_size, int item_count)
{
struct CACHE_HEADER *cache;
struct CACHED_GENERIC *p;
struct CACHED_GENERIC *q;
int i;
cache = (struct CACHE_HEADER*)
ntfs_malloc(sizeof(struct CACHE_HEADER)
+ item_count*full_item_size);
if (cache) {
cache->name = name;
cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC);
cache->reads = 0;
cache->writes = 0;
cache->hits = 0;
/* chain the entries, and mark an invalid entry */
cache->most_recent_entry = (struct CACHED_GENERIC*)NULL;
cache->free_entry = &cache->entry[0];
p = &cache->entry[0];
for (i=0; i<(item_count - 1); i++) {
q = (struct CACHED_GENERIC*)((char*)p + full_item_size);
p->next = q;
p->variable = (void*)NULL;
p->varsize = 0;
p = q;
}
/* special for the last entry */
p->next = (struct CACHED_GENERIC*)NULL;
p->variable = (void*)NULL;
p->varsize = 0;
}
return (cache);
}
/*
* Create all LRU caches
*
* No error return, if creation is not possible, cacheing will
* just be not available
*/
void ntfs_create_lru_caches(ntfs_volume *vol)
{
#if CACHE_INODE_SIZE
/* inode cache */
vol->xinode_cache = ntfs_create_cache("inode",
sizeof(struct CACHED_INODE), CACHE_INODE_SIZE);
#endif
vol->securid_cache = ntfs_create_cache("securid",
sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE);
#if CACHE_LEGACY_SIZE
vol->legacy_cache = ntfs_create_cache("legacy",
sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE);
#endif
}
/*
* Free all LRU caches
*/
void ntfs_free_lru_caches(ntfs_volume *vol)
{
#if CACHE_INODE_SIZE
ntfs_free_cache(vol->xinode_cache);
#endif
ntfs_free_cache(vol->securid_cache);
#if CACHE_LEGACY_SIZE
ntfs_free_cache(vol->legacy_cache);
#endif
}

View File

@ -61,6 +61,7 @@
#include "bitmap.h"
#include "security.h"
#include "acls.h"
#include "cache.h"
#include "misc.h"
/*
@ -2655,7 +2656,7 @@ int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
+ (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
ntfs_invalidate_cache(scx->vol->legacy_cache,
GENERIC(&legacy),
(cache_compare)leg_compare);
(cache_compare)leg_compare,0);
}
#endif
free(newattr);
@ -2853,7 +2854,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
#endif
ntfs_invalidate_cache(scx->vol->legacy_cache,
GENERIC(&legacy),
(cache_compare)leg_compare);
(cache_compare)leg_compare,0);
}
#endif
}
@ -3099,7 +3100,7 @@ int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
legacy.varsize = 0;
ntfs_invalidate_cache(scx->vol->legacy_cache,
GENERIC(&legacy),
(cache_compare)leg_compare);
(cache_compare)leg_compare,0);
}
#endif
free(attr);

View File

@ -65,6 +65,7 @@
#include "logfile.h"
#include "dir.h"
#include "logging.h"
#include "cache.h"
#include "misc.h"
const char *ntfs_home =