From d75f69d80e21d96e4d24516055c13307b63aaf5e Mon Sep 17 00:00:00 2001 From: jpandre Date: Fri, 18 Dec 2009 08:12:23 +0000 Subject: [PATCH] Cached inode data for subsequent use --- include/ntfs-3g/inode.h | 22 ++++- include/ntfs-3g/param.h | 1 + include/ntfs-3g/volume.h | 3 + libntfs-3g/attrib.c | 60 ++++++++++-- libntfs-3g/cache.c | 10 ++ libntfs-3g/dir.c | 42 +++++++- libntfs-3g/inode.c | 200 ++++++++++++++++++++++++++++++++++++--- libntfs-3g/mft.c | 4 + src/ntfs-3g.c | 16 +++- 9 files changed, 324 insertions(+), 34 deletions(-) diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index f0f62797..468c0f24 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -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 */ diff --git a/include/ntfs-3g/param.h b/include/ntfs-3g/param.h index 95fd7aa8..8d2b989f 100644 --- a/include/ntfs-3g/param.h +++ b/include/ntfs-3g/param.h @@ -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 */ diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index e695fe75..2e11c022 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -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 diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 7b20c08b..43bd74bb 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -43,6 +43,7 @@ #include #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); diff --git a/libntfs-3g/cache.c b/libntfs-3g/cache.c index 68aa2e0c..5e15fc0c 100644 --- a/libntfs-3g/cache.c +++ b/libntfs-3g/cache.c @@ -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 diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 7d7038e0..196e7d41 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -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); diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index bffa344f..a45b6bc9 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -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; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 5e27494a..28ad5d19 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -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; } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 3a2bd494..03451251 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -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 {