Reengineered LRU caches, made generic, and applied to finding inode numbers

This commit is contained in:
jpandre 2008-01-10 17:32:55 +00:00
parent 076358d6fd
commit 038156ba82
10 changed files with 690 additions and 400 deletions

View File

@ -75,7 +75,8 @@ extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid,
extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid,
ntfschar *name, u8 name_len, ntfschar *target, u8 target_len);
extern int ntfs_check_empty_dir(ntfs_inode *ni);
extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
extern int ntfs_delete(ntfs_volume *vol, const char *path,
ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
u8 name_len);
extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,

View File

@ -1,6 +1,46 @@
#ifndef _NTFS_MISC_H_
#define _NTFS_MISC_H_
#include "volume.h"
struct CACHED_GENERIC {
struct CACHED_GENERIC *next;
char *pathname;
void *fixed[0];
} ;
struct CACHED_INODE {
struct CACHED_INODE *next;
char *pathname;
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

@ -59,9 +59,10 @@ struct CACHED_PERMISSIONS {
*/
struct CACHED_PERMISSIONS_LEGACY {
struct CACHED_PERMISSIONS permissions;
struct CACHED_PERMISSIONS_LEGACY *next;
char *unused;
u64 mft_no;
struct CACHED_PERMISSIONS perm;
} ;
/*
@ -70,6 +71,7 @@ struct CACHED_PERMISSIONS_LEGACY {
struct CACHED_SECURID {
struct CACHED_SECURID *next;
void *unused;
uid_t uid;
gid_t gid;
unsigned int dmode;
@ -80,29 +82,20 @@ struct CACHED_SECURID {
* Header of the security cache
*/
struct SECURITY_HEAD {
struct CACHED_PERMISSIONS_HEADER {
unsigned int last;
struct CACHED_SECURID *first_securid;
struct CACHED_SECURID *most_recent_securid;
struct CACHED_PERMISSIONS_LEGACY *first_legacy;
struct CACHED_PERMISSIONS_LEGACY *most_recent_legacy;
/* statistics for permissions */
unsigned long p_writes;
unsigned long p_reads;
unsigned long p_hits;
/* statistics for securids */
unsigned long s_writes;
unsigned long s_reads;
unsigned long s_hits;
unsigned long s_hops;
} ;
/*
* The whole security cache
* The whole permissions cache
*/
struct SECURITY_CACHE {
struct SECURITY_HEAD head;
struct PERMISSIONS_CACHE {
struct CACHED_PERMISSIONS_HEADER head;
struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */
} ;
@ -122,7 +115,7 @@ struct SECURITY_CONTEXT {
ntfs_volume *vol;
struct MAPPING *usermapping;
struct MAPPING *groupmapping;
struct SECURITY_CACHE **pseccache;
struct PERMISSIONS_CACHE **pseccache;
uid_t uid; /* uid of user requesting (not the mounter) */
gid_t gid; /* gid of user requesting (not the mounter) */
} ;
@ -189,7 +182,7 @@ void ntfs_close_secure(struct SECURITY_CONTEXT *scx);
struct SECURITY_API {
u32 magic;
struct SECURITY_CONTEXT security;
struct SECURITY_CACHE *seccache;
struct PERMISSIONS_CACHE *seccache;
} ;
/*

View File

@ -42,6 +42,10 @@
#include <mntent.h>
#endif
#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */
#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */
#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */
/*
* Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY,
* so we define them ourselves.
@ -207,6 +211,16 @@ struct _ntfs_volume {
greatly improves statfs() performance */
s64 free_mft_records; /* Same for free mft records (see above) */
#if CACHE_INODE_SIZE
struct CACHE_HEADER *inode_cache;
#endif
#if CACHE_SECURID_SIZE
struct CACHE_HEADER *securid_cache;
#endif
#if CACHE_LEGACY_SIZE
struct CACHE_HEADER *legacy_cache;
#endif
/* Temp: for directory handling */
void *private_data; /* ntfs_dir for . */
void *private_bmp1; /* ntfs_bmp for $MFT/$BITMAP */

View File

@ -5,6 +5,7 @@
* Copyright (c) 2004-2005 Richard Russon
* Copyright (c) 2004-2006 Szabolcs Szakacsits
* Copyright (c) 2005-2006 Yura Pakhuchiy
* Copyright (c) 2008 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
@ -77,6 +78,62 @@ ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'),
ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'),
const_cpu_to_le16('\0') };
#if CACHE_INODE_SIZE
/*
* Pathname comparing for entering/fetching from cache
*/
static int inode_cache_compare(const struct CACHED_GENERIC *cached,
const struct CACHED_GENERIC *wanted)
{
return (strcmp(cached->pathname, wanted->pathname));
}
/*
* Pathname comparing for invalidating entries in cache
*
* A partial path is compared in order to invalidate all paths
* related to a renamed directory
*/
static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached,
const struct CACHED_GENERIC *wanted)
{
int len;
len = strlen(wanted->pathname);
return (strncmp(cached->pathname, wanted->pathname,len)
|| ((cached->pathname[len] != '\0')
&& (cached->pathname[len] != '/')));
}
/*
* Normalize file paths for cacheing
* Just remove leading and trailing '/', there should not be any
* non-standard components (such as "/../" or "/./") because
* paths have been rewritten by fuse.
*
* Returns the first non-'/' char in the original path
*/
static char *path_normalize(char *path)
{
int len;
char *p;
/* remove leading and trailing '/' even for root */
len = strlen(path);
while ((len > 1) && (path[len - 1] == PATH_SEP))
path[--len] = '\0';
p = path;
while (*p == PATH_SEP)
p++;
return (p);
}
#endif
/**
* ntfs_inode_lookup_by_name - find an inode in a directory given its name
* @dir_ni: ntfs inode of the directory in which to search for the name
@ -426,6 +483,11 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
ntfs_inode *result = NULL;
ntfschar *unicode = NULL;
char *ascii = NULL;
#if CACHE_INODE_SIZE
struct CACHED_INODE item;
struct CACHED_INODE *cached;
char *fullname;
#endif
if (!vol || !pathname) {
errno = EINVAL;
@ -434,36 +496,68 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
ntfs_log_trace("path: '%s'\n", pathname);
if (parent) {
ni = parent;
} else {
ni = ntfs_inode_open(vol, FILE_root);
if (!ni) {
ntfs_log_debug("Couldn't open the inode of the root "
"directory.\n");
err = EIO;
goto close;
}
}
unicode = ntfs_calloc(MAX_PATH);
ascii = strdup(pathname);
if (!unicode || !ascii) {
ntfs_log_debug("Out of memory.\n");
err = ENOMEM;
goto close;
goto out;
}
#if CACHE_INODE_SIZE
fullname = path_normalize(ascii);
p = fullname;
#else
p = ascii;
/* Remove leading /'s. */
while (p && *p && *p == PATH_SEP)
p++;
#endif
if (parent) {
ni = parent;
} else {
#if CACHE_INODE_SIZE
/*
* fetch inode for full path from cache
*/
if (*fullname) {
item.pathname = fullname;
cached = (struct CACHED_INODE*)ntfs_fetch_cache(
vol->inode_cache, GENERIC(&item),
inode_cache_compare);
} else
cached = (struct CACHED_INODE*)NULL;
if (cached) {
/*
* return opened inode if found in cache
*/
inum = MREF(cached->inum);
ni = ntfs_inode_open(vol, inum);
if (!ni) {
ntfs_log_debug("Cannot open inode %llu: %s.\n",
(unsigned long long)inum, p);
err = EIO;
}
result = ni;
goto out;
}
#endif
ni = ntfs_inode_open(vol, FILE_root);
if (!ni) {
ntfs_log_debug("Couldn't open the inode of the root "
"directory.\n");
err = EIO;
result = (ntfs_inode*)NULL;
goto out;
}
}
while (p && *p) {
/* Find the end of the first token. */
q = strchr(p, PATH_SEP);
if (q != NULL) {
*q = '\0';
q++;
/* q++; JPA */
}
len = ntfs_mbstoucs(p, &unicode, MAX_PATH);
@ -475,8 +569,33 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
err = ENAMETOOLONG;
goto close;
}
#if CACHE_INODE_SIZE
/*
* fetch inode for partial path from cache
* if not available, compute and store into cache
*/
if (parent)
inum = ntfs_inode_lookup_by_name(ni, unicode, len);
else {
item.pathname = fullname;
cached = (struct CACHED_INODE*)ntfs_fetch_cache(
vol->inode_cache, GENERIC(&item),
inode_cache_compare);
if (cached) {
inum = cached->inum;
} else {
inum = ntfs_inode_lookup_by_name(ni, unicode, len);
if (inum != (u64) -1) {
item.inum = inum;
ntfs_enter_cache(vol->inode_cache,
GENERIC(&item),
inode_cache_compare);
}
}
}
#else
inum = ntfs_inode_lookup_by_name(ni, unicode, len);
#endif
if (inum == (u64) -1) {
ntfs_log_debug("Couldn't find name '%s' in pathname "
"'%s'.\n", p, pathname);
@ -499,6 +618,7 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
goto close;
}
if (q) *q++ = PATH_SEP; /* JPA */
p = q;
while (p && *p && *p == PATH_SEP)
p++;
@ -1369,7 +1489,8 @@ no_hardlink:
*
* Return 0 on success or -1 on error with errno set to the error code.
*/
int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
int ntfs_delete(ntfs_volume *vol, const char *pathname,
ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
{
ntfs_attr_search_ctx *actx = NULL;
ntfs_index_context *ictx = NULL;
@ -1377,6 +1498,11 @@ int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
BOOL case_sensitive_match = TRUE;
int err = 0;
#if CACHE_INODE_SIZE
char *ascii;
struct CACHED_INODE item;
int count;
#endif
ntfs_log_trace("Entering.\n");
@ -1556,6 +1682,22 @@ out:
err = errno;
if (ntfs_inode_close(ni) && !err)
err = errno;
#if CACHE_INODE_SIZE
/* invalide cache entry, even if there was an error */
ascii = strdup(pathname);
if (ascii) {
char *p;
item.pathname = path_normalize(ascii);
count = ntfs_invalidate_cache(vol->inode_cache, GENERIC(&item),
inode_cache_inv_compare);
p = ascii; /* do not clear ascii */
free(p);
}
if (!ascii || !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,3 +1,26 @@
/**
* misc.c : miscellaneous :
* - dealing with errors in memory allocation
* - data caching
*
* Copyright (c) 2008 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
@ -5,7 +28,12 @@
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "types.h"
#include "security.h"
#include "misc.h"
#include "logging.h"
@ -34,3 +62,297 @@ void *ntfs_malloc(size_t 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 statitics is
* kept.
*/
current = cache->most_recent_entry;
previous = (struct CACHED_GENERIC*)NULL;
before = (struct CACHED_GENERIC*)NULL;
while (current
&& (!current->pathname
|| 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->pathname) {
current->pathname = ntfs_malloc(
strlen(item->pathname) + 1);
} else
current->pathname = (char*)NULL;
} else {
before->next = (struct CACHED_GENERIC*)NULL;
current = previous;
if (item->pathname) {
if (current->pathname)
current->pathname = realloc(
current->pathname,
strlen(item->pathname) + 1);
else
current->pathname = ntfs_malloc(
strlen(item->pathname) + 1);
} else {
if (current->pathname)
free(current->pathname);
current->pathname = (char*)NULL;
}
}
current->next = cache->most_recent_entry;
cache->most_recent_entry = current;
memcpy(current->fixed, item->fixed, cache->fixed_size);
if (item->pathname) {
if (current->pathname) {
strcpy(current->pathname,
item->pathname);
} 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->pathname = (char*)NULL;
}
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->pathname)
free(current->pathname);
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->pathname)
free(entry->pathname);
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->pathname = (char*)NULL;
p = q;
}
/* special for the last entry */
p->next = (struct CACHED_GENERIC*)NULL;
p->pathname = (char*)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->inode_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->inode_cache);
#endif
ntfs_free_cache(vol->securid_cache);
#if CACHE_LEGACY_SIZE
ntfs_free_cache(vol->legacy_cache);
#endif
}

View File

@ -32,10 +32,8 @@
#define BUFSZ 1024 /* buffer size to read mapping file */
#define MAPPINGFILE "/NTFS-3G/UserMapping" /* name of mapping file */
#define LINESZ 120 /* maximum useful size of a mapping line */
#define CACHE_SECURID_SIZE 16 /* securid cache, size >= 3 and not too big */
#define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */
#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */
#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */
#ifdef HAVE_CONFIG_H
#include "config.h"
@ -1631,75 +1629,28 @@ static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
* and 30% if the cache is disabled.
*/
static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
u32 securindex)
{
struct CACHED_SECURID *cachesecurid;
struct SECURITY_CACHE *cache;
#if CACHE_LEGACY_SIZE
struct CACHED_PERMISSIONS_LEGACY *cachelegacy;
#endif
struct PERMISSIONS_CACHE *cache;
unsigned int index1;
unsigned int i;
cache = (struct SECURITY_CACHE*)NULL;
/* create the securid cache first */
cachesecurid = (struct CACHED_SECURID*)
ntfs_malloc(CACHE_SECURID_SIZE*sizeof(struct CACHED_SECURID));
if (cachesecurid) {
/* chain the entries, and mark an invalid mode */
for (i=0; i<(CACHE_SECURID_SIZE - 1); i++) {
cachesecurid[i].next = &cachesecurid[i+1];
cachesecurid[i].dmode = -1;
}
/* special for the last entry */
cachesecurid[CACHE_SECURID_SIZE - 1].next =
(struct CACHED_SECURID*)NULL;
cachesecurid[CACHE_SECURID_SIZE - 1].dmode = -1;
#if CACHE_LEGACY_SIZE
/* create the legacy cache if needed */
cachelegacy = (struct CACHED_PERMISSIONS_LEGACY*)
ntfs_malloc(CACHE_LEGACY_SIZE*
sizeof(struct CACHED_PERMISSIONS_LEGACY));
if (cachelegacy) {
/* chain the entries, and mark an invalid entry */
for (i=0; i<(CACHE_LEGACY_SIZE - 1); i++) {
cachelegacy[i].next = &cachelegacy[i+1];
cachelegacy[i].permissions.valid = 0;
}
/* special for the last entry */
cachelegacy[CACHE_LEGACY_SIZE - 1].next =
(struct CACHED_PERMISSIONS_LEGACY*)NULL;
cachelegacy[CACHE_LEGACY_SIZE - 1].permissions.valid = 0;;
#endif
/* create the first permissions blocks */
index1 = securindex >> CACHE_PERMISSIONS_BITS;
cache = (struct SECURITY_CACHE*)
ntfs_malloc(sizeof(struct SECURITY_CACHE)
+ index1*sizeof(struct CACHED_PERMISSIONS*));
if (cache) {
cache->head.last = index1;
cache->head.p_reads = 0;
cache->head.p_hits = 0;
cache->head.p_writes = 0;
cache->head.s_reads = 0;
cache->head.s_hits = 0;
cache->head.s_writes = 0;
cache->head.s_hops = 0;
*scx->pseccache = cache;
cache->head.first_securid = cachesecurid;
cache->head.most_recent_securid = cachesecurid;
for (i=0; i<=index1; i++)
cache->cachetable[i]
= (struct CACHED_PERMISSIONS*)NULL;
#if CACHE_LEGACY_SIZE
cache->head.first_legacy = cachelegacy;
cache->head.most_recent_legacy = cachelegacy;
#endif
}
#if CACHE_LEGACY_SIZE
}
#endif
cache = (struct PERMISSIONS_CACHE*)NULL;
/* create the first permissions blocks */
index1 = securindex >> CACHE_PERMISSIONS_BITS;
cache = (struct PERMISSIONS_CACHE*)
ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
+ index1*sizeof(struct CACHED_PERMISSIONS*));
if (cache) {
cache->head.last = index1;
cache->head.p_reads = 0;
cache->head.p_hits = 0;
cache->head.p_writes = 0;
*scx->pseccache = cache;
for (i=0; i<=index1; i++)
cache->cachetable[i]
= (struct CACHED_PERMISSIONS*)NULL;
}
return (cache);
}
@ -1712,14 +1663,10 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
static void free_caches(struct SECURITY_CONTEXT *scx)
{
unsigned int index1;
struct SECURITY_CACHE *pseccache;
struct PERMISSIONS_CACHE *pseccache;
pseccache = *scx->pseccache;
if (pseccache) {
free(pseccache->head.first_securid);
#if CACHE_LEGACY_SIZE
free(pseccache->head.first_legacy);
#endif
for (index1=0; index1<=pseccache->head.last; index1++)
if (pseccache->cachetable[index1])
free(pseccache->cachetable[index1]);
@ -1727,244 +1674,20 @@ static void free_caches(struct SECURITY_CONTEXT *scx)
}
}
/*
* Fetch a securid from cache
* returns the cache entry, or NULL if not available
*/
static const struct CACHED_SECURID *fetch_securid(struct SECURITY_CONTEXT *scx,
uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
static int compare(const struct CACHED_SECURID *cached,
const struct CACHED_SECURID *item)
{
struct SECURITY_CACHE *cache;
struct CACHED_SECURID *current;
struct CACHED_SECURID *previous;
unsigned int dmode; /* mode and directory flag */
cache = *scx->pseccache;
if (cache) {
/*
* Search sequentially in LRU list
*/
dmode = (isdir ? mode | 010000 : mode);
current = cache->head.most_recent_securid;
previous = (struct CACHED_SECURID*)NULL;
while (current
&& ((current->uid != uid)
|| (current->gid != gid)
|| (current->dmode != dmode))) {
cache->head.s_hops++;
previous = current;
current = current->next;
}
if (current)
cache->head.s_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->head.most_recent_securid;
cache->head.most_recent_securid = current;
}
cache->head.s_reads++;
} else /* cache not ready */
current = (struct CACHED_SECURID*)NULL;
return (current);
return (((cached->uid != item->uid)
|| (cached->gid != item->gid)
|| (cached->dmode != item->dmode)));
}
/*
* Enter a securid into cache
* returns the cache entry
*/
static const struct CACHED_SECURID *enter_securid(struct SECURITY_CONTEXT *scx,
uid_t uid, gid_t gid, mode_t mode,
BOOL isdir, le32 securid)
static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
const struct CACHED_PERMISSIONS_LEGACY *item)
{
struct SECURITY_CACHE *cache;
struct CACHED_SECURID *current;
struct CACHED_SECURID *previous;
struct CACHED_SECURID *before;
unsigned int dmode;
dmode = mode & 07777;
if (isdir) dmode |= 010000;
cache = *scx->pseccache;
if (cache || (cache = create_caches(scx, le32_to_cpu(securid)))) {
/*
* Search sequentially in LRU list to locate the end,
* and find out whether the entry is already in list
* As we normally go to the end, no statitics is
* kept.
*/
current = cache->head.most_recent_securid;
previous = (struct CACHED_SECURID*)NULL;
before = (struct CACHED_SECURID*)NULL;
while (current
&& ((current->uid != uid)
|| (current->gid != gid)
|| (current->dmode != dmode))) {
before = previous;
previous = current;
current = current->next;
}
if (!current) {
/*
* Not in list, reuse the last entry,
* and relink as head of list
* Note : we assume at least three entries, so
* before, previous and first are always different
*/
before->next = (struct CACHED_SECURID*)NULL;
previous->next = cache->head.most_recent_securid;
cache->head.most_recent_securid = previous;
current = previous;
current->uid = uid;
current->gid = gid;
current->dmode = dmode;
current->securid = securid;
}
cache->head.s_writes++;
} else /* cache not available */
current = (struct CACHED_SECURID*)NULL;
return (current);
return (cached->mft_no != item->mft_no);
}
#if CACHE_LEGACY_SIZE
/*
* Fetch a legacy directory from cache
* returns the cache entry, or NULL if not available
*/
static struct CACHED_PERMISSIONS *fetch_legacy(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni)
{
struct SECURITY_CACHE *cache;
struct CACHED_PERMISSIONS_LEGACY *current;
struct CACHED_PERMISSIONS_LEGACY *previous;
struct CACHED_PERMISSIONS *cacheentry;
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
cache = *scx->pseccache;
if (cache
&& (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
/*
* Search sequentially in LRU list
*/
current = cache->head.most_recent_legacy;
previous = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
while (current
&& (!current->permissions.valid
|| (current->mft_no != ni->mft_no))) {
previous = current;
current = current->next;
}
if (current) {
cache->head.p_hits++;
cacheentry = &current->permissions;
}
if (current && previous) {
/*
* found and not at head of list, unlink from current
* position and relink as head of list
*/
previous->next = current->next;
current->next = cache->head.most_recent_legacy;
cache->head.most_recent_legacy = current;
}
cache->head.p_reads++;
}
return (cacheentry);
}
/*
* Enter a legacy directory into cache
* returns the cache entry
*/
static struct CACHED_PERMISSIONS *enter_legacy(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni, uid_t uid, gid_t gid,
mode_t mode)
{
struct SECURITY_CACHE *cache;
struct CACHED_PERMISSIONS_LEGACY *current;
struct CACHED_PERMISSIONS_LEGACY *previous;
struct CACHED_PERMISSIONS_LEGACY *before;
struct CACHED_PERMISSIONS *cacheentry;
mode &= 07777;
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
cache = *scx->pseccache;
if ((cache || (cache = create_caches(scx, FIRST_SECURITY_ID)))
&& (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
/*
* Search sequentially in LRU list to locate the end,
* and find out whether the entry is already in list
* As we normally go to the end, no statitics is
* kept.
*/
current = cache->head.most_recent_legacy;
previous = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
before = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
while (current
&& (!current->permissions.valid
|| (current->mft_no != ni->mft_no))) {
before = previous;
previous = current;
current = current->next;
}
if (!current) {
/*
* Not in list, reuse the last entry,
* and relink as head of list
* Note : we assume at least three entries, so
* before, previous and first are always different
*/
before->next = (struct CACHED_PERMISSIONS_LEGACY*)NULL;
previous->next = cache->head.most_recent_legacy;
cache->head.most_recent_legacy = previous;
current = previous;
current->mft_no = ni->mft_no;
cacheentry = &current->permissions;
cacheentry->uid = uid;
cacheentry->gid = gid;
cacheentry->mode = mode;
cacheentry->inh_fileid = cpu_to_le32(0);
cacheentry->inh_dirid = cpu_to_le32(0);
cacheentry->valid = 1;
}
cache->head.p_writes++;
}
return (cacheentry);
}
/*
* Invalidate a legacy directory entry in cache
* This is needed after a change in directory protection, when
* no security id has been assigned (only for NTFS 1.x)
*
* returns the cache entry, or NULL if not available
*/
static struct CACHED_PERMISSIONS *invalidate_legacy(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni)
{
struct CACHED_PERMISSIONS *cached;
cached = fetch_legacy(scx, ni);
if (cached) cached->valid = 0;
return (cached);
}
#endif
/*
* Resize permission cache table
* do not call unless resizing is needed
@ -1977,8 +1700,8 @@ static struct CACHED_PERMISSIONS *invalidate_legacy(struct SECURITY_CONTEXT *scx
static void resize_cache(struct SECURITY_CONTEXT *scx,
u32 securindex)
{
struct SECURITY_CACHE *oldcache;
struct SECURITY_CACHE *newcache;
struct PERMISSIONS_CACHE *oldcache;
struct PERMISSIONS_CACHE *newcache;
int newcnt;
int oldcnt;
unsigned int index1;
@ -1993,13 +1716,13 @@ static void resize_cache(struct SECURITY_CONTEXT *scx,
/* expand cache beyond current end, do not use realloc() */
/* to avoid losing data when there is no more memory */
oldcnt = oldcache->head.last + 1;
newcache = (struct SECURITY_CACHE*)
newcache = (struct PERMISSIONS_CACHE*)
ntfs_malloc(
sizeof(struct SECURITY_CACHE)
sizeof(struct PERMISSIONS_CACHE)
+ (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
if (newcache) {
memcpy(newcache,oldcache,
sizeof(struct SECURITY_CACHE)
sizeof(struct PERMISSIONS_CACHE)
+ (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
free(oldcache);
/* mark new entries as not valid */
@ -2025,7 +1748,7 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
{
struct CACHED_PERMISSIONS *cacheentry;
struct CACHED_PERMISSIONS *cacheblock;
struct SECURITY_CACHE *pcache;
struct PERMISSIONS_CACHE *pcache;
u32 securindex;
unsigned int index1;
unsigned int index2;
@ -2084,12 +1807,27 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
} else
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
}
} else
#if CACHE_LEGACY_SIZE
cacheentry = enter_legacy(scx, ni, uid, gid, mode);
#else
} else {
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
#if CACHE_LEGACY_SIZE
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
struct CACHED_PERMISSIONS_LEGACY wanted;
struct CACHED_PERMISSIONS_LEGACY *legacy;
wanted.perm.uid = uid;
wanted.perm.gid = gid;
wanted.perm.mode = mode & 07777;
wanted.perm.inh_fileid = cpu_to_le32(0);
wanted.perm.inh_dirid = cpu_to_le32(0);
wanted.mft_no = ni->mft_no;
wanted.unused = (char*)NULL;
legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
scx->vol->legacy_cache, GENERIC(&wanted),
(cache_compare)leg_compare);
if (legacy) cacheentry = &legacy->perm;
}
#endif
}
return (cacheentry);
}
@ -2106,7 +1844,7 @@ static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
ntfs_inode *ni)
{
struct CACHED_PERMISSIONS *cacheentry;
struct SECURITY_CACHE *pcache;
struct PERMISSIONS_CACHE *pcache;
u32 securindex;
unsigned int index1;
unsigned int index2;
@ -2133,8 +1871,20 @@ static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
}
}
#if CACHE_LEGACY_SIZE
else
cacheentry = fetch_legacy(scx, ni);
else {
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
struct CACHED_PERMISSIONS_LEGACY wanted;
struct CACHED_PERMISSIONS_LEGACY *legacy;
wanted.mft_no = ni->mft_no;
wanted.unused = (char*)NULL;
legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
scx->vol->legacy_cache, GENERIC(&wanted),
(cache_compare)leg_compare);
if (legacy) cacheentry = &legacy->perm;
}
}
#endif
return (cacheentry);
}
@ -3310,6 +3060,7 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
{
const struct CACHED_SECURID *cached;
struct CACHED_SECURID wanted;
char *newattr;
int newattrsz;
const SID *usid;
@ -3321,7 +3072,14 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
#if !FORCE_FORMAT_v1x
/* check whether target securid is known in cache */
cached = fetch_securid(scx, uid, gid, mode & 07777, isdir);
wanted.uid = uid;
wanted.gid = gid;
wanted.dmode = mode & 07777;
if (isdir) wanted.dmode |= 0x10000;
wanted.unused = (char*)NULL;
cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
scx->vol->securid_cache, GENERIC(&wanted),
(cache_compare)compare);
/* quite simple, if we are lucky */
if (cached)
securid = cached->securid;
@ -3344,9 +3102,10 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
newattrsz);
if (securid) {
/* update cache, for subsequent use */
enter_securid(scx, uid,
gid, mode, isdir,
securid);
wanted.securid = securid;
ntfs_enter_cache(scx->vol->securid_cache,
GENERIC(&wanted),
(cache_compare)compare);
}
free(newattr);
} else {
@ -3373,6 +3132,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
{
int res;
const struct CACHED_SECURID *cached;
struct CACHED_SECURID wanted;
char *newattr;
const SID *usid;
const SID *gsid;
@ -3384,7 +3144,14 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != 0;
if (test_nino_flag(ni, v3_Extensions)) {
cached = fetch_securid(scx, uid, gid, mode & 07777, isdir);
wanted.uid = uid;
wanted.gid = gid;
wanted.dmode = mode & 07777;
if (isdir) wanted.dmode |= 0x10000;
wanted.unused = (char*)NULL;
cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
scx->vol->securid_cache, GENERIC(&wanted),
(cache_compare)compare);
/* quite simple, if we are lucky */
if (cached) {
ni->security_id = cached->securid;
@ -3411,14 +3178,23 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
res = update_secur_descr(scx->vol, newattr, ni);
if (!res) {
/* update cache, for subsequent use */
if (test_nino_flag(ni, v3_Extensions))
enter_securid(scx, uid,
gid, mode, isdir,
ni->security_id);
if (test_nino_flag(ni, v3_Extensions)) {
wanted.securid = ni->security_id;
ntfs_enter_cache(scx->vol->securid_cache,
GENERIC(&wanted),
(cache_compare)compare);
}
#if CACHE_LEGACY_SIZE
/* also invalidate legacy cache */
if (isdir && !ni->security_id)
invalidate_legacy(scx, ni);
if (isdir && !ni->security_id) {
struct CACHED_PERMISSIONS_LEGACY legacy;
legacy.mft_no = ni->mft_no;
legacy.unused = (char*)NULL;
ntfs_invalidate_cache(scx->vol->legacy_cache,
GENERIC(&legacy),
(cache_compare)leg_compare);
}
#endif
}
free(newattr);

View File

@ -123,7 +123,10 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
* @err_val if an invalid character is found in @name1 during the comparison.
*
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
*
* A few optimizations made by JPA
*/
int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
const ntfschar *name2, const u32 name2_len,
const int err_val __attribute__((unused)),
@ -139,21 +142,29 @@ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
exit(1);
}
#endif
for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) {
c1 = le16_to_cpu(*name1);
name1++;
c2 = le16_to_cpu(*name2);
name2++;
if (ic) {
if (c1 < upcase_len)
c1 = le16_to_cpu(upcase[c1]);
if (c2 < upcase_len)
c2 = le16_to_cpu(upcase[c2]);
}
#if 0
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
#endif
cnt = min(name1_len, name2_len);
/* JPA average loop count is 8 */
if (cnt > 0) {
if (ic)
/* JPA this loop in 76% cases */
do {
c1 = le16_to_cpu(*name1);
name1++;
c2 = le16_to_cpu(*name2);
name2++;
if (c1 < upcase_len)
c1 = le16_to_cpu(upcase[c1]);
if (c2 < upcase_len)
c2 = le16_to_cpu(upcase[c2]);
} while ((c1 == c2) && --cnt);
else
do {
/* JPA this loop in 24% cases */
c1 = le16_to_cpu(*name1);
name1++;
c2 = le16_to_cpu(*name2);
name2++;
} while ((c1 == c2) && --cnt);
if (c1 < c2)
return -1;
if (c1 > c2)
@ -163,12 +174,6 @@ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
return -1;
if (name1_len == name2_len)
return 0;
/* name1_len > name2_len */
#if 0
c1 = le16_to_cpu(*name1);
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
#endif
return 1;
}

View File

@ -152,6 +152,7 @@ static int __ntfs_volume_release(ntfs_volume *v)
ntfs_error_set(&err);
}
ntfs_free_lru_caches(v);
free(v->vol_name);
free(v->upcase);
free(v->attrdef);
@ -1166,6 +1167,7 @@ ntfs_volume *ntfs_mount(const char *name __attribute__((unused)),
ntfs_device_free(dev);
errno = eo;
}
ntfs_create_lru_caches(vol);
return vol;
#else
/*

View File

@ -131,7 +131,7 @@ typedef struct {
struct fuse_chan *fc;
BOOL inherit;
BOOL addsecurids;
struct SECURITY_CACHE *seccache;
struct PERMISSIONS_CACHE *seccache;
struct SECURITY_CONTEXT security;
} ntfs_fuse_context_t;
@ -842,7 +842,7 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size)
}
static int ntfs_fuse_ftruncate(const char *org_path, off_t size,
struct fuse_file_info *fi)
struct fuse_file_info *fi __attribute__((unused)))
{
/*
* in ->ftruncate() the file handle is guaranteed
@ -1290,7 +1290,8 @@ static int ntfs_fuse_rm(const char *org_path)
|| ntfs_allowed_access(&security, path, dir_ni,
S_IEXEC + S_IWRITE + S_ISVTX)) {
if (ntfs_delete(ni, dir_ni, uname, uname_len))
if (ntfs_delete(ctx->vol, org_path, ni, dir_ni,
uname, uname_len))
res = -errno;
/* ntfs_delete() always closes ni and dir_ni */
ni = dir_ni = NULL;
@ -1857,21 +1858,6 @@ static void ntfs_close(void)
1000 * ctx->seccache->head.p_hits
/ ctx->seccache->head.p_reads % 10);
}
if (ctx->seccache && ctx->seccache->head.s_reads) {
ntfs_log_info("Security id cache : %lu writes "
"%lu reads %lu.%1lu%% hits "
"%lu.%1lu mean hops\n",
ctx->seccache->head.s_writes,
ctx->seccache->head.s_reads,
100 * ctx->seccache->head.s_hits
/ ctx->seccache->head.s_reads,
1000 * ctx->seccache->head.s_hits
/ ctx->seccache->head.s_reads % 10,
ctx->seccache->head.s_hops
/ ctx->seccache->head.s_reads,
10 * ctx->seccache->head.s_hops
/ ctx->seccache->head.s_reads % 10);
}
}
ntfs_close_secure(&security);
}
@ -2529,6 +2515,15 @@ static struct fuse *mount_fuse(char *parsed_options)
goto err;
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache") == -1)
goto err;
#if CACHE_INODE_SIZE
/*
* JPA fuse attribute cacheing is not useful if we
* cache inodes, and this avoids hard link problems
*/
if (fuse_opt_add_arg(&args,
"-oattr_timeout=0,ac_attr_timeout=0") == -1)
goto err;
#endif
if (ctx->debug)
if (fuse_opt_add_arg(&args, "-odebug") == -1)
goto err;
@ -2631,7 +2626,7 @@ int main(int argc, char *argv[])
}
}
ctx->seccache = (struct SECURITY_CACHE*)NULL;
ctx->seccache = (struct PERMISSIONS_CACHE*)NULL;
ntfs_log_info("Version %s\n", VERSION);
ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n",