diff --git a/include/ntfs-3g/Makefile.am b/include/ntfs-3g/Makefile.am index 11a4f7d6..f4f72d69 100644 --- a/include/ntfs-3g/Makefile.am +++ b/include/ntfs-3g/Makefile.am @@ -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 \ diff --git a/include/ntfs-3g/cache.h b/include/ntfs-3g/cache.h new file mode 100644 index 00000000..67c33251 --- /dev/null +++ b/include/ntfs-3g/cache.h @@ -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_ */ + diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index 494763dc..c23171e9 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -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 */ diff --git a/include/ntfs-3g/misc.h b/include/ntfs-3g/misc.h index dbdee9a9..a03e964e 100644 --- a/include/ntfs-3g/misc.h +++ b/include/ntfs-3g/misc.h @@ -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); diff --git a/include/ntfs-3g/security.h b/include/ntfs-3g/security.h index c7710ca2..e91feec8 100644 --- a/include/ntfs-3g/security.h +++ b/include/ntfs-3g/security.h @@ -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" */ diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index e09616b6..fe2fb9da 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -23,6 +23,7 @@ libntfs_3g_la_SOURCES = \ attrlist.c \ bitmap.c \ bootsect.c \ + cache.c \ collate.c \ compat.c \ compress.c \ diff --git a/libntfs-3g/cache.c b/libntfs-3g/cache.c new file mode 100644 index 00000000..68aa2e0c --- /dev/null +++ b/libntfs-3g/cache.c @@ -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 +#endif +#ifdef HAVE_STRING_H +#include +#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; ifree_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 +} diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index d557656a..7d7038e0 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -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)); diff --git a/libntfs-3g/misc.c b/libntfs-3g/misc.c index 8e662a6a..b2e17cbf 100644 --- a/libntfs-3g/misc.c +++ b/libntfs-3g/misc.c @@ -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 -} diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index 362924e6..bc8db903 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -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); diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index fd25f331..5cdb4da6 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -65,6 +65,7 @@ #include "logfile.h" #include "dir.h" #include "logging.h" +#include "cache.h" #include "misc.h" const char *ntfs_home =