mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-27 12:03:42 +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,
|
extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid,
|
||||||
ntfschar *name, u8 name_len, ntfschar *target, u8 target_len);
|
ntfschar *name, u8 name_len, ntfschar *target, u8 target_len);
|
||||||
extern int ntfs_check_empty_dir(ntfs_inode *ni);
|
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);
|
u8 name_len);
|
||||||
|
|
||||||
extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||||
|
@ -1,6 +1,46 @@
|
|||||||
#ifndef _NTFS_MISC_H_
|
#ifndef _NTFS_MISC_H_
|
||||||
#define _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_calloc(size_t size);
|
||||||
void *ntfs_malloc(size_t size);
|
void *ntfs_malloc(size_t size);
|
||||||
|
|
||||||
|
@ -59,9 +59,10 @@ struct CACHED_PERMISSIONS {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct CACHED_PERMISSIONS_LEGACY {
|
struct CACHED_PERMISSIONS_LEGACY {
|
||||||
struct CACHED_PERMISSIONS permissions;
|
|
||||||
struct CACHED_PERMISSIONS_LEGACY *next;
|
struct CACHED_PERMISSIONS_LEGACY *next;
|
||||||
|
char *unused;
|
||||||
u64 mft_no;
|
u64 mft_no;
|
||||||
|
struct CACHED_PERMISSIONS perm;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -70,6 +71,7 @@ struct CACHED_PERMISSIONS_LEGACY {
|
|||||||
|
|
||||||
struct CACHED_SECURID {
|
struct CACHED_SECURID {
|
||||||
struct CACHED_SECURID *next;
|
struct CACHED_SECURID *next;
|
||||||
|
void *unused;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
unsigned int dmode;
|
unsigned int dmode;
|
||||||
@ -80,29 +82,20 @@ struct CACHED_SECURID {
|
|||||||
* Header of the security cache
|
* Header of the security cache
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct SECURITY_HEAD {
|
struct CACHED_PERMISSIONS_HEADER {
|
||||||
unsigned int last;
|
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 */
|
/* statistics for permissions */
|
||||||
unsigned long p_writes;
|
unsigned long p_writes;
|
||||||
unsigned long p_reads;
|
unsigned long p_reads;
|
||||||
unsigned long p_hits;
|
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 PERMISSIONS_CACHE {
|
||||||
struct SECURITY_HEAD head;
|
struct CACHED_PERMISSIONS_HEADER head;
|
||||||
struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */
|
struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
@ -122,7 +115,7 @@ struct SECURITY_CONTEXT {
|
|||||||
ntfs_volume *vol;
|
ntfs_volume *vol;
|
||||||
struct MAPPING *usermapping;
|
struct MAPPING *usermapping;
|
||||||
struct MAPPING *groupmapping;
|
struct MAPPING *groupmapping;
|
||||||
struct SECURITY_CACHE **pseccache;
|
struct PERMISSIONS_CACHE **pseccache;
|
||||||
uid_t uid; /* uid of user requesting (not the mounter) */
|
uid_t uid; /* uid of user requesting (not the mounter) */
|
||||||
gid_t gid; /* gid 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 {
|
struct SECURITY_API {
|
||||||
u32 magic;
|
u32 magic;
|
||||||
struct SECURITY_CONTEXT security;
|
struct SECURITY_CONTEXT security;
|
||||||
struct SECURITY_CACHE *seccache;
|
struct PERMISSIONS_CACHE *seccache;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -42,6 +42,10 @@
|
|||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
#endif
|
#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,
|
* Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY,
|
||||||
* so we define them ourselves.
|
* so we define them ourselves.
|
||||||
@ -207,6 +211,16 @@ struct _ntfs_volume {
|
|||||||
greatly improves statfs() performance */
|
greatly improves statfs() performance */
|
||||||
s64 free_mft_records; /* Same for free mft records (see above) */
|
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 */
|
/* Temp: for directory handling */
|
||||||
void *private_data; /* ntfs_dir for . */
|
void *private_data; /* ntfs_dir for . */
|
||||||
void *private_bmp1; /* ntfs_bmp for $MFT/$BITMAP */
|
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-2005 Richard Russon
|
||||||
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
* 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
|
* 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
|
* 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'),
|
ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'),
|
||||||
const_cpu_to_le16('\0') };
|
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
|
* 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
|
* @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;
|
ntfs_inode *result = NULL;
|
||||||
ntfschar *unicode = NULL;
|
ntfschar *unicode = NULL;
|
||||||
char *ascii = NULL;
|
char *ascii = NULL;
|
||||||
|
#if CACHE_INODE_SIZE
|
||||||
|
struct CACHED_INODE item;
|
||||||
|
struct CACHED_INODE *cached;
|
||||||
|
char *fullname;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!vol || !pathname) {
|
if (!vol || !pathname) {
|
||||||
errno = EINVAL;
|
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);
|
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);
|
unicode = ntfs_calloc(MAX_PATH);
|
||||||
ascii = strdup(pathname);
|
ascii = strdup(pathname);
|
||||||
if (!unicode || !ascii) {
|
if (!unicode || !ascii) {
|
||||||
ntfs_log_debug("Out of memory.\n");
|
ntfs_log_debug("Out of memory.\n");
|
||||||
err = ENOMEM;
|
err = ENOMEM;
|
||||||
goto close;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CACHE_INODE_SIZE
|
||||||
|
fullname = path_normalize(ascii);
|
||||||
|
p = fullname;
|
||||||
|
#else
|
||||||
p = ascii;
|
p = ascii;
|
||||||
/* Remove leading /'s. */
|
/* Remove leading /'s. */
|
||||||
while (p && *p && *p == PATH_SEP)
|
while (p && *p && *p == PATH_SEP)
|
||||||
p++;
|
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) {
|
while (p && *p) {
|
||||||
/* Find the end of the first token. */
|
/* Find the end of the first token. */
|
||||||
q = strchr(p, PATH_SEP);
|
q = strchr(p, PATH_SEP);
|
||||||
if (q != NULL) {
|
if (q != NULL) {
|
||||||
*q = '\0';
|
*q = '\0';
|
||||||
q++;
|
/* q++; JPA */
|
||||||
}
|
}
|
||||||
|
|
||||||
len = ntfs_mbstoucs(p, &unicode, MAX_PATH);
|
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;
|
err = ENAMETOOLONG;
|
||||||
goto close;
|
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);
|
inum = ntfs_inode_lookup_by_name(ni, unicode, len);
|
||||||
|
#endif
|
||||||
if (inum == (u64) -1) {
|
if (inum == (u64) -1) {
|
||||||
ntfs_log_debug("Couldn't find name '%s' in pathname "
|
ntfs_log_debug("Couldn't find name '%s' in pathname "
|
||||||
"'%s'.\n", p, pathname);
|
"'%s'.\n", p, pathname);
|
||||||
@ -499,6 +618,7 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
|
|||||||
goto close;
|
goto close;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (q) *q++ = PATH_SEP; /* JPA */
|
||||||
p = q;
|
p = q;
|
||||||
while (p && *p && *p == PATH_SEP)
|
while (p && *p && *p == PATH_SEP)
|
||||||
p++;
|
p++;
|
||||||
@ -1369,7 +1489,8 @@ no_hardlink:
|
|||||||
*
|
*
|
||||||
* Return 0 on success or -1 on error with errno set to the error code.
|
* 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_attr_search_ctx *actx = NULL;
|
||||||
ntfs_index_context *ictx = 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 looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
|
||||||
BOOL case_sensitive_match = TRUE;
|
BOOL case_sensitive_match = TRUE;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
#if CACHE_INODE_SIZE
|
||||||
|
char *ascii;
|
||||||
|
struct CACHED_INODE item;
|
||||||
|
int count;
|
||||||
|
#endif
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
ntfs_log_trace("Entering.\n");
|
||||||
|
|
||||||
@ -1556,6 +1682,22 @@ out:
|
|||||||
err = errno;
|
err = errno;
|
||||||
if (ntfs_inode_close(ni) && !err)
|
if (ntfs_inode_close(ni) && !err)
|
||||||
err = errno;
|
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) {
|
if (err) {
|
||||||
errno = err;
|
errno = err;
|
||||||
ntfs_log_debug("Could not delete file: %s\n", strerror(errno));
|
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
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
@ -5,7 +28,12 @@
|
|||||||
#ifdef HAVE_STDLIB_H
|
#ifdef HAVE_STDLIB_H
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "security.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
@ -34,3 +62,297 @@ void *ntfs_malloc(size_t size)
|
|||||||
return p;
|
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 BUFSZ 1024 /* buffer size to read mapping file */
|
||||||
#define MAPPINGFILE "/NTFS-3G/UserMapping" /* name of mapping file */
|
#define MAPPINGFILE "/NTFS-3G/UserMapping" /* name of mapping file */
|
||||||
#define LINESZ 120 /* maximum useful size of a mapping line */
|
#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_BITS 6 /* log2 of unitary allocation of permissions */
|
||||||
#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable 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
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "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.
|
* 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)
|
u32 securindex)
|
||||||
{
|
{
|
||||||
struct CACHED_SECURID *cachesecurid;
|
struct PERMISSIONS_CACHE *cache;
|
||||||
struct SECURITY_CACHE *cache;
|
|
||||||
#if CACHE_LEGACY_SIZE
|
|
||||||
struct CACHED_PERMISSIONS_LEGACY *cachelegacy;
|
|
||||||
#endif
|
|
||||||
unsigned int index1;
|
unsigned int index1;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
cache = (struct SECURITY_CACHE*)NULL;
|
cache = (struct PERMISSIONS_CACHE*)NULL;
|
||||||
/* create the securid cache first */
|
/* create the first permissions blocks */
|
||||||
cachesecurid = (struct CACHED_SECURID*)
|
index1 = securindex >> CACHE_PERMISSIONS_BITS;
|
||||||
ntfs_malloc(CACHE_SECURID_SIZE*sizeof(struct CACHED_SECURID));
|
cache = (struct PERMISSIONS_CACHE*)
|
||||||
if (cachesecurid) {
|
ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
|
||||||
/* chain the entries, and mark an invalid mode */
|
+ index1*sizeof(struct CACHED_PERMISSIONS*));
|
||||||
for (i=0; i<(CACHE_SECURID_SIZE - 1); i++) {
|
if (cache) {
|
||||||
cachesecurid[i].next = &cachesecurid[i+1];
|
cache->head.last = index1;
|
||||||
cachesecurid[i].dmode = -1;
|
cache->head.p_reads = 0;
|
||||||
}
|
cache->head.p_hits = 0;
|
||||||
/* special for the last entry */
|
cache->head.p_writes = 0;
|
||||||
cachesecurid[CACHE_SECURID_SIZE - 1].next =
|
*scx->pseccache = cache;
|
||||||
(struct CACHED_SECURID*)NULL;
|
for (i=0; i<=index1; i++)
|
||||||
cachesecurid[CACHE_SECURID_SIZE - 1].dmode = -1;
|
cache->cachetable[i]
|
||||||
#if CACHE_LEGACY_SIZE
|
= (struct CACHED_PERMISSIONS*)NULL;
|
||||||
/* 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
|
|
||||||
}
|
}
|
||||||
return (cache);
|
return (cache);
|
||||||
}
|
}
|
||||||
@ -1712,14 +1663,10 @@ static struct SECURITY_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
|
|||||||
static void free_caches(struct SECURITY_CONTEXT *scx)
|
static void free_caches(struct SECURITY_CONTEXT *scx)
|
||||||
{
|
{
|
||||||
unsigned int index1;
|
unsigned int index1;
|
||||||
struct SECURITY_CACHE *pseccache;
|
struct PERMISSIONS_CACHE *pseccache;
|
||||||
|
|
||||||
pseccache = *scx->pseccache;
|
pseccache = *scx->pseccache;
|
||||||
if (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++)
|
for (index1=0; index1<=pseccache->head.last; index1++)
|
||||||
if (pseccache->cachetable[index1])
|
if (pseccache->cachetable[index1])
|
||||||
free(pseccache->cachetable[index1]);
|
free(pseccache->cachetable[index1]);
|
||||||
@ -1727,244 +1674,20 @@ static void free_caches(struct SECURITY_CONTEXT *scx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int compare(const struct CACHED_SECURID *cached,
|
||||||
* Fetch a securid from cache
|
const struct CACHED_SECURID *item)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
struct SECURITY_CACHE *cache;
|
return (((cached->uid != item->uid)
|
||||||
struct CACHED_SECURID *current;
|
|| (cached->gid != item->gid)
|
||||||
struct CACHED_SECURID *previous;
|
|| (cached->dmode != item->dmode)));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
|
||||||
* Enter a securid into cache
|
const struct CACHED_PERMISSIONS_LEGACY *item)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
struct SECURITY_CACHE *cache;
|
return (cached->mft_no != item->mft_no);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#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
|
* Resize permission cache table
|
||||||
* do not call unless resizing is needed
|
* 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,
|
static void resize_cache(struct SECURITY_CONTEXT *scx,
|
||||||
u32 securindex)
|
u32 securindex)
|
||||||
{
|
{
|
||||||
struct SECURITY_CACHE *oldcache;
|
struct PERMISSIONS_CACHE *oldcache;
|
||||||
struct SECURITY_CACHE *newcache;
|
struct PERMISSIONS_CACHE *newcache;
|
||||||
int newcnt;
|
int newcnt;
|
||||||
int oldcnt;
|
int oldcnt;
|
||||||
unsigned int index1;
|
unsigned int index1;
|
||||||
@ -1993,13 +1716,13 @@ static void resize_cache(struct SECURITY_CONTEXT *scx,
|
|||||||
/* expand cache beyond current end, do not use realloc() */
|
/* expand cache beyond current end, do not use realloc() */
|
||||||
/* to avoid losing data when there is no more memory */
|
/* to avoid losing data when there is no more memory */
|
||||||
oldcnt = oldcache->head.last + 1;
|
oldcnt = oldcache->head.last + 1;
|
||||||
newcache = (struct SECURITY_CACHE*)
|
newcache = (struct PERMISSIONS_CACHE*)
|
||||||
ntfs_malloc(
|
ntfs_malloc(
|
||||||
sizeof(struct SECURITY_CACHE)
|
sizeof(struct PERMISSIONS_CACHE)
|
||||||
+ (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
|
+ (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
|
||||||
if (newcache) {
|
if (newcache) {
|
||||||
memcpy(newcache,oldcache,
|
memcpy(newcache,oldcache,
|
||||||
sizeof(struct SECURITY_CACHE)
|
sizeof(struct PERMISSIONS_CACHE)
|
||||||
+ (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
|
+ (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
|
||||||
free(oldcache);
|
free(oldcache);
|
||||||
/* mark new entries as not valid */
|
/* 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 *cacheentry;
|
||||||
struct CACHED_PERMISSIONS *cacheblock;
|
struct CACHED_PERMISSIONS *cacheblock;
|
||||||
struct SECURITY_CACHE *pcache;
|
struct PERMISSIONS_CACHE *pcache;
|
||||||
u32 securindex;
|
u32 securindex;
|
||||||
unsigned int index1;
|
unsigned int index1;
|
||||||
unsigned int index2;
|
unsigned int index2;
|
||||||
@ -2084,12 +1807,27 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
|
|||||||
} else
|
} else
|
||||||
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
|
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
#if CACHE_LEGACY_SIZE
|
|
||||||
cacheentry = enter_legacy(scx, ni, uid, gid, mode);
|
|
||||||
#else
|
|
||||||
cacheentry = (struct CACHED_PERMISSIONS*)NULL;
|
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
|
#endif
|
||||||
|
}
|
||||||
return (cacheentry);
|
return (cacheentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2106,7 +1844,7 @@ static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
|
|||||||
ntfs_inode *ni)
|
ntfs_inode *ni)
|
||||||
{
|
{
|
||||||
struct CACHED_PERMISSIONS *cacheentry;
|
struct CACHED_PERMISSIONS *cacheentry;
|
||||||
struct SECURITY_CACHE *pcache;
|
struct PERMISSIONS_CACHE *pcache;
|
||||||
u32 securindex;
|
u32 securindex;
|
||||||
unsigned int index1;
|
unsigned int index1;
|
||||||
unsigned int index2;
|
unsigned int index2;
|
||||||
@ -2133,8 +1871,20 @@ static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if CACHE_LEGACY_SIZE
|
#if CACHE_LEGACY_SIZE
|
||||||
else
|
else {
|
||||||
cacheentry = fetch_legacy(scx, ni);
|
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
|
#endif
|
||||||
return (cacheentry);
|
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)
|
uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
|
||||||
{
|
{
|
||||||
const struct CACHED_SECURID *cached;
|
const struct CACHED_SECURID *cached;
|
||||||
|
struct CACHED_SECURID wanted;
|
||||||
char *newattr;
|
char *newattr;
|
||||||
int newattrsz;
|
int newattrsz;
|
||||||
const SID *usid;
|
const SID *usid;
|
||||||
@ -3321,7 +3072,14 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
|
|||||||
#if !FORCE_FORMAT_v1x
|
#if !FORCE_FORMAT_v1x
|
||||||
/* check whether target securid is known in cache */
|
/* 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 */
|
/* quite simple, if we are lucky */
|
||||||
if (cached)
|
if (cached)
|
||||||
securid = cached->securid;
|
securid = cached->securid;
|
||||||
@ -3344,9 +3102,10 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
|
|||||||
newattrsz);
|
newattrsz);
|
||||||
if (securid) {
|
if (securid) {
|
||||||
/* update cache, for subsequent use */
|
/* update cache, for subsequent use */
|
||||||
enter_securid(scx, uid,
|
wanted.securid = securid;
|
||||||
gid, mode, isdir,
|
ntfs_enter_cache(scx->vol->securid_cache,
|
||||||
securid);
|
GENERIC(&wanted),
|
||||||
|
(cache_compare)compare);
|
||||||
}
|
}
|
||||||
free(newattr);
|
free(newattr);
|
||||||
} else {
|
} else {
|
||||||
@ -3373,6 +3132,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
|||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
const struct CACHED_SECURID *cached;
|
const struct CACHED_SECURID *cached;
|
||||||
|
struct CACHED_SECURID wanted;
|
||||||
char *newattr;
|
char *newattr;
|
||||||
const SID *usid;
|
const SID *usid;
|
||||||
const SID *gsid;
|
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;
|
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != 0;
|
||||||
if (test_nino_flag(ni, v3_Extensions)) {
|
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 */
|
/* quite simple, if we are lucky */
|
||||||
if (cached) {
|
if (cached) {
|
||||||
ni->security_id = cached->securid;
|
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);
|
res = update_secur_descr(scx->vol, newattr, ni);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
/* update cache, for subsequent use */
|
/* update cache, for subsequent use */
|
||||||
if (test_nino_flag(ni, v3_Extensions))
|
if (test_nino_flag(ni, v3_Extensions)) {
|
||||||
enter_securid(scx, uid,
|
wanted.securid = ni->security_id;
|
||||||
gid, mode, isdir,
|
ntfs_enter_cache(scx->vol->securid_cache,
|
||||||
ni->security_id);
|
GENERIC(&wanted),
|
||||||
|
(cache_compare)compare);
|
||||||
|
}
|
||||||
#if CACHE_LEGACY_SIZE
|
#if CACHE_LEGACY_SIZE
|
||||||
/* also invalidate legacy cache */
|
/* also invalidate legacy cache */
|
||||||
if (isdir && !ni->security_id)
|
if (isdir && !ni->security_id) {
|
||||||
invalidate_legacy(scx, ni);
|
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
|
#endif
|
||||||
}
|
}
|
||||||
free(newattr);
|
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.
|
* @err_val if an invalid character is found in @name1 during the comparison.
|
||||||
*
|
*
|
||||||
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
|
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
|
||||||
|
*
|
||||||
|
* A few optimizations made by JPA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
||||||
const ntfschar *name2, const u32 name2_len,
|
const ntfschar *name2, const u32 name2_len,
|
||||||
const int err_val __attribute__((unused)),
|
const int err_val __attribute__((unused)),
|
||||||
@ -139,21 +142,29 @@ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) {
|
cnt = min(name1_len, name2_len);
|
||||||
c1 = le16_to_cpu(*name1);
|
/* JPA average loop count is 8 */
|
||||||
name1++;
|
if (cnt > 0) {
|
||||||
c2 = le16_to_cpu(*name2);
|
if (ic)
|
||||||
name2++;
|
/* JPA this loop in 76% cases */
|
||||||
if (ic) {
|
do {
|
||||||
if (c1 < upcase_len)
|
c1 = le16_to_cpu(*name1);
|
||||||
c1 = le16_to_cpu(upcase[c1]);
|
name1++;
|
||||||
if (c2 < upcase_len)
|
c2 = le16_to_cpu(*name2);
|
||||||
c2 = le16_to_cpu(upcase[c2]);
|
name2++;
|
||||||
}
|
if (c1 < upcase_len)
|
||||||
#if 0
|
c1 = le16_to_cpu(upcase[c1]);
|
||||||
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
|
if (c2 < upcase_len)
|
||||||
return err_val;
|
c2 = le16_to_cpu(upcase[c2]);
|
||||||
#endif
|
} 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)
|
if (c1 < c2)
|
||||||
return -1;
|
return -1;
|
||||||
if (c1 > c2)
|
if (c1 > c2)
|
||||||
@ -163,12 +174,6 @@ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
|||||||
return -1;
|
return -1;
|
||||||
if (name1_len == name2_len)
|
if (name1_len == name2_len)
|
||||||
return 0;
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +152,7 @@ static int __ntfs_volume_release(ntfs_volume *v)
|
|||||||
ntfs_error_set(&err);
|
ntfs_error_set(&err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ntfs_free_lru_caches(v);
|
||||||
free(v->vol_name);
|
free(v->vol_name);
|
||||||
free(v->upcase);
|
free(v->upcase);
|
||||||
free(v->attrdef);
|
free(v->attrdef);
|
||||||
@ -1166,6 +1167,7 @@ ntfs_volume *ntfs_mount(const char *name __attribute__((unused)),
|
|||||||
ntfs_device_free(dev);
|
ntfs_device_free(dev);
|
||||||
errno = eo;
|
errno = eo;
|
||||||
}
|
}
|
||||||
|
ntfs_create_lru_caches(vol);
|
||||||
return vol;
|
return vol;
|
||||||
#else
|
#else
|
||||||
/*
|
/*
|
||||||
|
@ -131,7 +131,7 @@ typedef struct {
|
|||||||
struct fuse_chan *fc;
|
struct fuse_chan *fc;
|
||||||
BOOL inherit;
|
BOOL inherit;
|
||||||
BOOL addsecurids;
|
BOOL addsecurids;
|
||||||
struct SECURITY_CACHE *seccache;
|
struct PERMISSIONS_CACHE *seccache;
|
||||||
struct SECURITY_CONTEXT security;
|
struct SECURITY_CONTEXT security;
|
||||||
} ntfs_fuse_context_t;
|
} 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,
|
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
|
* 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,
|
|| ntfs_allowed_access(&security, path, dir_ni,
|
||||||
S_IEXEC + S_IWRITE + S_ISVTX)) {
|
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;
|
res = -errno;
|
||||||
/* ntfs_delete() always closes ni and dir_ni */
|
/* ntfs_delete() always closes ni and dir_ni */
|
||||||
ni = dir_ni = NULL;
|
ni = dir_ni = NULL;
|
||||||
@ -1857,21 +1858,6 @@ static void ntfs_close(void)
|
|||||||
1000 * ctx->seccache->head.p_hits
|
1000 * ctx->seccache->head.p_hits
|
||||||
/ ctx->seccache->head.p_reads % 10);
|
/ 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);
|
ntfs_close_secure(&security);
|
||||||
}
|
}
|
||||||
@ -2529,6 +2515,15 @@ static struct fuse *mount_fuse(char *parsed_options)
|
|||||||
goto err;
|
goto err;
|
||||||
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache") == -1)
|
if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache") == -1)
|
||||||
goto err;
|
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 (ctx->debug)
|
||||||
if (fuse_opt_add_arg(&args, "-odebug") == -1)
|
if (fuse_opt_add_arg(&args, "-odebug") == -1)
|
||||||
goto err;
|
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("Version %s\n", VERSION);
|
||||||
ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n",
|
ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n",
|
||||||
|
Loading…
Reference in New Issue
Block a user