Cached inode data for subsequent use

PERMISSION_HANDLING_BRANCH
jpandre 2009-12-18 08:12:23 +00:00
parent 41a371f4a7
commit d75f69d80e
9 changed files with 324 additions and 34 deletions

View File

@ -50,6 +50,7 @@ typedef enum {
in the index. */
NI_v3_Extensions, /* 1: JPA v3.x extensions present. */
NI_TimesSet, /* 1: Use times which were set */
NI_KnownSize, /* 1: Set if sizes are meaningful */
} ntfs_inode_state_bits;
#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state)
@ -135,8 +136,11 @@ struct _ntfs_inode {
* These two fields are used to sync filename index and guaranteed to be
* correct, however value in index itself maybe wrong (windows itself
* do not update them properly).
* For directories, they hold the index size, provided the
* flag KnownSize is set.
*/
s64 data_size; /* Data size of unnamed DATA attribute. */
s64 data_size; /* Data size of unnamed DATA attribute
(or INDEX_ROOT for directories) */
s64 allocated_size; /* Allocated size stored in the filename
index. (NOTE: Equal to allocated size of
the unnamed data attribute for normal or
@ -179,6 +183,18 @@ extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref);
extern int ntfs_inode_close(ntfs_inode *ni);
extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni);
#if CACHE_NIDATA_SIZE
struct CACHED_GENERIC;
extern int ntfs_inode_real_close(ntfs_inode *ni);
extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref);
extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached);
extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item);
#endif
extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni,
const MFT_REF mref);
@ -201,4 +217,8 @@ extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size);
extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value,
size_t size, int flags);
/* debugging */
#define debug_double_inode(num, type)
#define debug_cached_inode(ni)
#endif /* defined _NTFS_INODE_H */

View File

@ -23,6 +23,7 @@
#define _NTFS_PARAM_H
#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */
#define CACHE_NIDATA_SIZE 64 /* idata 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 */

View File

@ -232,6 +232,9 @@ struct _ntfs_volume {
#if CACHE_INODE_SIZE
struct CACHE_HEADER *xinode_cache;
#endif
#if CACHE_NIDATA_SIZE
struct CACHE_HEADER *nidata_cache;
#endif
#if CACHE_SECURID_SIZE
struct CACHE_HEADER *securid_cache;
#endif

View File

@ -43,6 +43,7 @@
#include <limits.h>
#endif
#include "param.h"
#include "compat.h"
#include "attrib.h"
#include "attrlist.h"
@ -1463,6 +1464,15 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
goto err_out;
}
na->initialized_size = pos + count;
#if CACHE_NIDATA_SIZE
if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
: na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
na->ni->allocated_size = na->allocated_size;
set_nino_flag(na->ni,KnownSize);
}
#endif
ntfs_attr_put_search_ctx(ctx);
ctx = NULL;
/*
@ -1876,6 +1886,15 @@ retry:
if (!NVolReadOnly(vol)) {
written = ntfs_compressed_close(na, rl, ofs);
#if CACHE_NIDATA_SIZE
if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
: na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
na->ni->allocated_size = na->allocated_size;
set_nino_flag(na->ni,KnownSize);
}
#endif
/* If everything ok, update progress counters and continue. */
if (!written)
goto done;
@ -3217,9 +3236,12 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
goto put_err_out;
}
}
if (type == AT_DATA && name == AT_UNNAMED) {
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30
: type == AT_DATA && name == AT_UNNAMED) {
ni->data_size = size;
ni->allocated_size = (size + 7) & ~7;
set_nino_flag(ni,KnownSize);
}
ntfs_inode_mark_dirty(ni);
ntfs_attr_put_search_ctx(ctx);
@ -4341,10 +4363,14 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
if ((na->data_flags & ATTR_COMPRESSION_MASK)
|| NAttrSparse(na))
na->compressed_size = na->allocated_size;
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
: na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
na->ni->allocated_size = na->allocated_size;
NInoFileNameSetDirty(na->ni);
set_nino_flag(na->ni,KnownSize);
if (na->type == AT_DATA)
NInoFileNameSetDirty(na->ni);
}
goto resize_done;
}
@ -5244,9 +5270,17 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize)
ctx->attr->initialized_size = cpu_to_sle64(newsize);
}
/* Update data size in the index. */
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
NInoFileNameSetDirty(na->ni);
if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) {
na->ni->data_size = na->data_size;
na->ni->allocated_size = na->allocated_size;
set_nino_flag(na->ni,KnownSize);
}
} else {
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
NInoFileNameSetDirty(na->ni);
}
}
/* If the attribute now has zero size, make it resident. */
@ -5435,9 +5469,17 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize)
na->data_size = newsize;
ctx->attr->data_size = cpu_to_sle64(newsize);
/* Update data size in the index. */
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
NInoFileNameSetDirty(na->ni);
if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) {
na->ni->data_size = na->data_size;
na->ni->allocated_size = na->allocated_size;
set_nino_flag(na->ni,KnownSize);
}
} else {
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
NInoFileNameSetDirty(na->ni);
}
}
/* Set the inode dirty so it is written out later. */
ntfs_inode_mark_dirty(ctx->ntfs_ino);

View File

@ -564,6 +564,13 @@ void ntfs_create_lru_caches(ntfs_volume *vol)
vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL,
ntfs_dir_inode_hash, sizeof(struct CACHED_INODE),
CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE);
#endif
#if CACHE_NIDATA_SIZE
/* idata cache */
vol->nidata_cache = ntfs_create_cache("nidata",
ntfs_inode_nidata_free, ntfs_inode_nidata_hash,
sizeof(struct CACHED_NIDATA),
CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE);
#endif
vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL,
(cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0);
@ -581,6 +588,9 @@ void ntfs_free_lru_caches(ntfs_volume *vol)
{
#if CACHE_INODE_SIZE
ntfs_free_cache(vol->xinode_cache);
#endif
#if CACHE_NIDATA_SIZE
ntfs_free_cache(vol->nidata_cache);
#endif
ntfs_free_cache(vol->securid_cache);
#if CACHE_LEGACY_SIZE

View File

@ -1216,6 +1216,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
ni = ntfs_mft_record_alloc(dir_ni->vol, NULL);
if (!ni)
return NULL;
#if CACHE_NIDATA_SIZE
ntfs_inode_invalidate(dir_ni->vol, ni->mft_no);
#endif
/*
* Create STANDARD_INFORMATION attribute.
* JPA Depending on available inherited security descriptor,
@ -1379,8 +1382,12 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
fn->last_access_time = utc2ntfs(ni->last_access_time);
fn->data_size = cpu_to_sle64(ni->data_size);
fn->allocated_size = cpu_to_sle64(ni->allocated_size);
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
fn->data_size = fn->allocated_size = const_cpu_to_le64(0);
else {
fn->data_size = cpu_to_sle64(ni->data_size);
fn->allocated_size = cpu_to_sle64(ni->allocated_size);
}
memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
/* Add FILE_NAME attribute to inode. */
if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) {
@ -1541,6 +1548,9 @@ int ntfs_delete(ntfs_volume *vol, const char *pathname,
BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
BOOL case_sensitive_match = TRUE;
int err = 0;
#if CACHE_NIDATA_SIZE
int i;
#endif
#if CACHE_INODE_SIZE
struct CACHED_INODE item;
const char *p;
@ -1731,12 +1741,31 @@ search:
"Probably leaving inconsistent metadata.\n");
}
/* All extents should be attached after attribute walk. */
#if CACHE_NIDATA_SIZE
/*
* Disconnect extents before deleting them, so they are
* not wrongly moved to cache through the chainings
*/
for (i=ni->nr_extents-1; i>=0; i--) {
ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL;
ni->extent_nis[i]->nr_extents = 0;
if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) {
err = errno;
ntfs_log_error("Failed to free extent MFT record. "
"Leaving inconsistent metadata.\n");
}
}
free(ni->extent_nis);
ni->nr_extents = 0;
ni->extent_nis = (ntfs_inode**)NULL;
#else
while (ni->nr_extents)
if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) {
err = errno;
ntfs_log_error("Failed to free extent MFT record. "
"Leaving inconsistent metadata.\n");
}
#endif
if (ntfs_mft_record_free(ni->vol, ni)) {
err = errno;
ntfs_log_error("Failed to free base MFT record. "
@ -1812,10 +1841,13 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
fn->file_name_length = name_len;
fn->file_name_type = nametype;
fn->file_attributes = ni->flags;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT;
fn->allocated_size = cpu_to_sle64(ni->allocated_size);
fn->data_size = cpu_to_sle64(ni->data_size);
fn->data_size = fn->allocated_size = const_cpu_to_le64(0);
} else {
fn->allocated_size = cpu_to_sle64(ni->allocated_size);
fn->data_size = cpu_to_sle64(ni->data_size);
}
fn->creation_time = utc2ntfs(ni->creation_time);
fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);

View File

@ -43,8 +43,10 @@
#include "param.h"
#include "compat.h"
#include "types.h"
#include "attrib.h"
#include "volume.h"
#include "cache.h"
#include "inode.h"
#include "attrib.h"
#include "debug.h"
#include "mft.h"
#include "attrlist.h"
@ -154,7 +156,7 @@ static void __ntfs_inode_release(ntfs_inode *ni)
* Return a pointer to the ntfs_inode structure on success or NULL on error,
* with errno set to the error code.
*/
ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
{
s64 l;
ntfs_inode *ni = NULL;
@ -207,13 +209,13 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
ni->usn = std_info->usn;
} else {
clear_nino_flag(ni, v3_Extensions);
ni->owner_id = 0;
ni->security_id = 0;
ni->owner_id = const_cpu_to_le32(0);
ni->security_id = const_cpu_to_le32(0);
}
/* Set attribute list information. */
olderrno = errno;
if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0,
ctx)) {
if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0,
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
if (errno != ENOENT)
goto put_err_out;
/* Attribute list attribute does not present. */
@ -268,6 +270,7 @@ get_size:
ni->data_size = le32_to_cpu(ctx->attr->value_length);
ni->allocated_size = (ni->data_size + 7) & ~7;
}
set_nino_flag(ni,KnownSize);
}
ntfs_attr_put_search_ctx(ctx);
out:
@ -306,7 +309,8 @@ err_out:
* EINVAL @ni is invalid (probably it is an extent inode).
* EIO I/O error while trying to write inode to disk.
*/
int ntfs_inode_close(ntfs_inode *ni)
int ntfs_inode_real_close(ntfs_inode *ni)
{
int ret = -1;
@ -326,7 +330,7 @@ int ntfs_inode_close(ntfs_inode *ni)
/* Is this a base inode with mapped extent inodes? */
if (ni->nr_extents > 0) {
while (ni->nr_extents > 0) {
if (ntfs_inode_close(ni->extent_nis[0])) {
if (ntfs_inode_real_close(ni->extent_nis[0])) {
if (errno != EIO)
errno = EBUSY;
goto err;
@ -366,8 +370,10 @@ int ntfs_inode_close(ntfs_inode *ni)
/* Ignore errors, they don't really matter. */
if (tmp_nis)
base_ni->extent_nis = tmp_nis;
} else if (tmp_nis)
} else if (tmp_nis) {
free(tmp_nis);
base_ni->extent_nis = (ntfs_inode**)NULL;
}
/* Allow for error checking. */
i = -1;
break;
@ -389,6 +395,154 @@ err:
return ret;
}
#if CACHE_NIDATA_SIZE
/*
* Free an inode structure when there is not more space
* in the cache
*/
void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached)
{
ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni);
}
/*
* Compute a hash value for an inode entry
*/
int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item)
{
return (((const struct CACHED_NIDATA*)item)->inum
% (2*CACHE_NIDATA_SIZE));
}
/*
* inum comparing for entering/fetching from cache
*/
static int idata_cache_compare(const struct CACHED_GENERIC *cached,
const struct CACHED_GENERIC *wanted)
{
return (((const struct CACHED_NIDATA*)cached)->inum
!= ((const struct CACHED_NIDATA*)wanted)->inum);
}
/*
* Invalidate an inode entry when not needed anymore.
* The entry should have been synced, it may be reused later,
* if it is requested before it is dropped from cache.
*/
void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref)
{
struct CACHED_NIDATA item;
int count;
item.inum = MREF(mref);
item.ni = (ntfs_inode*)NULL;
item.pathname = (const char*)NULL;
item.varsize = 0;
count = ntfs_invalidate_cache(vol->nidata_cache,
GENERIC(&item),idata_cache_compare,CACHE_FREE);
}
#endif
/*
* Open an inode
*
* When possible, an entry recorded in the cache is reused
*
* **NEVER REOPEN** an inode, this can lead to a duplicated
* cache entry (hard to detect), and to an obsolete one being
* reused. System files are however protected from being cached.
*/
ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
{
ntfs_inode *ni;
#if CACHE_NIDATA_SIZE
struct CACHED_NIDATA item;
struct CACHED_NIDATA *cached;
/* fetch idata from cache */
item.inum = MREF(mref);
debug_double_inode(item.inum,1);
item.pathname = (const char*)NULL;
item.varsize = 0;
cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache,
GENERIC(&item),idata_cache_compare);
if (cached) {
ni = cached->ni;
/* do not keep open entries in cache */
ntfs_remove_cache(vol->nidata_cache,
(struct CACHED_GENERIC*)cached,0);
} else {
ni = ntfs_inode_real_open(vol, mref);
}
#else
ni = ntfs_inode_real_open(vol, mref);
#endif
return (ni);
}
/*
* Close an inode entry
*
* If cacheing is in use, the entry is synced and kept available
* in cache for further use.
*
* System files (inode < 16 or having the IS_4 flag) are protected
* against being cached.
*/
int ntfs_inode_close(ntfs_inode *ni)
{
int res;
#if CACHE_NIDATA_SIZE
BOOL dirty;
struct CACHED_NIDATA item;
if (ni) {
debug_double_inode(ni->mft_no,0);
/* do not cache system files : could lead to double entries */
if (ni->vol && ni->vol->nidata_cache
&& ((ni->mft_no == FILE_root)
|| ((ni->mft_no >= FILE_first_user)
&& !(ni->mrec->flags & MFT_RECORD_IS_4)))) {
/* If we have dirty metadata, write it out. */
dirty = NInoDirty(ni) || NInoAttrListDirty(ni);
if (dirty) {
res = ntfs_inode_sync(ni);
/* do a real close if sync failed */
if (res)
ntfs_inode_real_close(ni);
} else
res = 0;
if (!res) {
/* feed idata into cache */
item.inum = ni->mft_no;
item.ni = ni;
item.pathname = (const char*)NULL;
item.varsize = 0;
debug_cached_inode(ni);
ntfs_enter_cache(ni->vol->nidata_cache,
GENERIC(&item), idata_cache_compare);
}
} else {
/* cache not ready or system file, really close */
res = ntfs_inode_real_close(ni);
}
} else
res = 0;
#else
res = ntfs_inode_real_close(ni);
#endif
return (res);
}
/**
* ntfs_extent_inode_open - load an extent inode and attach it to its base
* @base_ni: base ntfs inode
@ -669,8 +823,13 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni)
fnx->file_attributes =
(fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) |
(ni->flags & FILE_ATTR_VALID_FLAGS);
fnx->allocated_size = cpu_to_sle64(ni->allocated_size);
fnx->data_size = cpu_to_sle64(ni->data_size);
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
fnx->data_size = fnx->allocated_size
= const_cpu_to_le64(0);
else {
fnx->allocated_size = cpu_to_sle64(ni->allocated_size);
fnx->data_size = cpu_to_sle64(ni->data_size);
}
if (!test_nino_flag(ni, TimesSet)) {
fnx->creation_time = utc2ntfs(ni->creation_time);
fnx->last_data_change_time = utc2ntfs(ni->last_data_change_time);
@ -1327,14 +1486,27 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size,
/*
* Mark times set to avoid overwriting
* them when the inode is closed.
* The inode structure must also be updated
* (with loss of precision) because of cacheing.
* TODO : use NTFS precision in inode, and
* return sub-second times in getattr()
*/
set_nino_flag(ni, TimesSet);
std_info->creation_time = cpu_to_le64(times[0]);
if (size >= 16)
ni->creation_time
= ntfs2utc(std_info->creation_time);
if (size >= 16) {
std_info->last_data_change_time = cpu_to_le64(times[1]);
if (size >= 24)
ni->last_data_change_time
= ntfs2utc(std_info->last_data_change_time);
}
if (size >= 24) {
std_info->last_access_time = cpu_to_le64(times[2]);
ni->last_access_time
= ntfs2utc(std_info->last_access_time);
}
std_info->last_mft_change_time = now;
ni->last_mft_change_time = ntfs2utc(now);
ntfs_inode_mark_dirty(ctx->ntfs_ino);
NInoFileNameSetDirty(ni);
@ -1365,7 +1537,7 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size,
}
}
ntfs_attr_put_search_ctx(ctx);
}
}
} else
if (size < 8)
errno = ERANGE;

View File

@ -1860,7 +1860,11 @@ int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni)
}
/* Throw away the now freed inode. */
#if CACHE_NIDATA_SIZE
if (!ntfs_inode_real_close(ni)) {
#else
if (!ntfs_inode_close(ni)) {
#endif
vol->free_mft_records++;
return 0;
}

View File

@ -714,12 +714,18 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
} else {
/* Directory. */
stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask);
na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
if (na) {
stbuf->st_size = na->data_size;
stbuf->st_blocks = na->allocated_size >> 9;
ntfs_attr_close(na);
/* get index size, if not known */
if (!test_nino_flag(ni, KnownSize)) {
na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
if (na) {
ni->data_size = na->data_size;
ni->allocated_size = na->allocated_size;
set_nino_flag(ni, KnownSize);
ntfs_attr_close(na);
}
}
stbuf->st_size = ni->data_size;
stbuf->st_blocks = ni->allocated_size >> 9;
stbuf->st_nlink = 1; /* Make find(1) work */
}
} else {