Redesigned caches for indexing cached entries
parent
7c88ccb95b
commit
41a371f4a7
|
@ -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 \
|
||||
|
|
|
@ -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_ */
|
||||
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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" */
|
||||
|
|
|
@ -23,6 +23,7 @@ libntfs_3g_la_SOURCES = \
|
|||
attrlist.c \
|
||||
bitmap.c \
|
||||
bootsect.c \
|
||||
cache.c \
|
||||
collate.c \
|
||||
compat.c \
|
||||
compress.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 <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
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "logfile.h"
|
||||
#include "dir.h"
|
||||
#include "logging.h"
|
||||
#include "cache.h"
|
||||
#include "misc.h"
|
||||
|
||||
const char *ntfs_home =
|
||||
|
|
Loading…
Reference in New Issue