mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-23 10:04:00 +08:00
Reengineered LRU caches, made generic, and applied to finding inode numbers
This commit is contained in:
parent
076358d6fd
commit
038156ba82
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
} ;
|
||||
|
||||
/*
|
||||
|
@ -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 */
|
||||
|
174
libntfs-3g/dir.c
174
libntfs-3g/dir.c
@ -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));
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 = ¤t->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 = ¤t->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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
/*
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user