From c0e8e211f3b047285b192046d8249246e81174f3 Mon Sep 17 00:00:00 2001 From: Yura Pakhuchiy Date: Thu, 20 Sep 2007 14:00:55 +0300 Subject: [PATCH] libntfs: Cache opened inodes. --- include/ntfs/inode.h | 17 +++++++++++++++- include/ntfs/volume.h | 7 +++++++ libntfs/inode.c | 45 +++++++++++++++++++++++++++++++++++++++++-- libntfs/mft.c | 7 +++++-- libntfs/volume.c | 11 ++++++++++- 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/include/ntfs/inode.h b/include/ntfs/inode.h index 70d172fd..0e1b0f2d 100644 --- a/include/ntfs/inode.h +++ b/include/ntfs/inode.h @@ -27,6 +27,7 @@ /* Forward declaration */ typedef struct _ntfs_inode ntfs_inode; +#include "list.h" #include "types.h" #include "layout.h" #include "support.h" @@ -132,7 +133,7 @@ struct _ntfs_inode { /* Below fields are valid only for base inode. */ /* - * This two fields are used to sync filename index and guaranteed to be + * 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). */ @@ -144,12 +145,26 @@ struct _ntfs_inode { of the unnamed data attribute for sparse or compressed files.) */ + /* + * These four fields are copy of relevant fields from + * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME + * attribute in the index. + */ time_t creation_time; time_t last_data_change_time; time_t last_mft_change_time; time_t last_access_time; + + /* These 2 fields are used to keep track of opened inodes. */ + struct list_head list_entry; /* Keep pointers to the next/prev list + entry. */ + int nr_references; /* How many times this inode was + opened. We really close inode only + when this reaches zero. */ }; +extern void __ntfs_inode_add_to_cache(ntfs_inode *ni); + extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); diff --git a/include/ntfs/volume.h b/include/ntfs/volume.h index a6948af0..caad93a1 100644 --- a/include/ntfs/volume.h +++ b/include/ntfs/volume.h @@ -44,6 +44,7 @@ /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; +#include "list.h" #include "types.h" #include "support.h" #include "device.h" @@ -131,6 +132,9 @@ typedef enum { #define NTFS_BUF_SIZE 8192 +#define NTFS_INODE_CACHE_SIZE 512 /* WARNING: This should be power of 2. */ +#define NTFS_INODE_CACHE_SIZE_BITS (NTFS_INODE_CACHE_SIZE - 1) + /** * struct _ntfs_volume - structure describing an open volume in memory. */ @@ -211,6 +215,9 @@ struct _ntfs_volume { long nr_free_clusters; /* This two are self explaining. */ long nr_free_mft_records; + + struct list_head inode_cache[NTFS_INODE_CACHE_SIZE]; /* List of opened + inodes. */ }; extern ntfs_volume *ntfs_volume_alloc(void); diff --git a/libntfs/inode.c b/libntfs/inode.c index a18a60be..b21404cc 100644 --- a/libntfs/inode.c +++ b/libntfs/inode.c @@ -99,6 +99,16 @@ static int __ntfs_inode_release(ntfs_inode *ni) return 0; } +/** + * __ntfs_inode_add_to_cache - do not use me! Only for internal library use. + */ +void __ntfs_inode_add_to_cache(ntfs_inode *ni) +{ + list_add_tail(&ni->list_entry, &ni->vol->inode_cache[ + ni->mft_no & NTFS_INODE_CACHE_SIZE_BITS]); + ni->nr_references = 1; +} + /** * ntfs_inode_open - open an inode ready for access * @vol: volume to get the inode from @@ -129,12 +139,27 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) ntfs_attr_search_ctx *ctx; int err = 0; STANDARD_INFORMATION *std_info; + struct list_head *pos; ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref)); if (!vol) { errno = EINVAL; return NULL; } + /* Check cache, maybe this inode already opened? */ + list_for_each(pos, &vol->inode_cache[MREF(mref) & + NTFS_INODE_CACHE_SIZE_BITS]) { + ntfs_inode *tmp_ni; + + tmp_ni = list_entry(pos, ntfs_inode, list_entry); + if (tmp_ni->mft_no == MREF(mref)) { + ntfs_log_trace("Found this inode in cache, increment " + "reference count and return it.\n"); + tmp_ni->nr_references++; + return tmp_ni; + } + } + /* Search failed. Properly open inode. */ ni = __ntfs_inode_allocate(vol); if (!ni) return NULL; @@ -212,6 +237,7 @@ get_size: } } ntfs_attr_put_search_ctx(ctx); + __ntfs_inode_add_to_cache(ni); return ni; put_err_out: if (!err) @@ -254,6 +280,18 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + /* Decrement number of users. If there are left then just return. */ + if (ni->nr_extents != -1) { + ni->nr_references--; + if (ni->nr_references) { + ntfs_log_trace("There are %d more references left to " + "this inode.\n", + ni->nr_references); + return 0; + } else + ntfs_log_trace("There are no more references left to " + "this inode.\n"); + } /* If we have dirty metadata, write it out. */ if (NInoDirty(ni) || NInoAttrListDirty(ni)) { if (ntfs_inode_sync(ni)) { @@ -312,9 +350,12 @@ int ntfs_inode_close(ntfs_inode *ni) break; } if (i != -1) - ntfs_log_debug("Extent inode was not attached to base inode! " - "Weird! Continuing regardless.\n"); + ntfs_log_debug("Extent inode was not attached to base " + "inode! Continuing regardless.\n"); } + /* Remove inode from the list of opened inodes. */ + if (ni->nr_extents != -1) + list_del(&ni->list_entry); return __ntfs_inode_release(ni); } diff --git a/libntfs/mft.c b/libntfs/mft.c index c0a3cae4..29e059a4 100644 --- a/libntfs/mft.c +++ b/libntfs/mft.c @@ -1460,9 +1460,12 @@ mft_rec_already_initialized: ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = ni->last_access_time = time(NULL); - /* Update the default mft allocation position if it was used. */ - if (!base_ni) + if (!base_ni) { + /* Update the default mft allocation position if it was used. */ vol->mft_data_pos = bit + 1; + /* Add inode to cache. */ + __ntfs_inode_add_to_cache(ni); + } /* Return the opened, allocated inode of the allocated mft record. */ ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n", base_ni ? "extent " : "", (long long)bit); diff --git a/libntfs/volume.c b/libntfs/volume.c index ecf813e2..24fb9369 100644 --- a/libntfs/volume.c +++ b/libntfs/volume.c @@ -76,7 +76,15 @@ */ ntfs_volume *ntfs_volume_alloc(void) { - return calloc(1, sizeof(ntfs_volume)); + ntfs_volume *vol; + int i; + + vol = calloc(1, sizeof(ntfs_volume)); + if (vol) { + for (i = 0; i < NTFS_INODE_CACHE_SIZE; i++) + INIT_LIST_HEAD(&vol->inode_cache[i]); + } + return vol; } /** @@ -162,6 +170,7 @@ static int ntfs_mft_load(ntfs_volume *vol) } vol->mft_ni->mft_no = 0; vol->mft_ni->mrec = mb; + __ntfs_inode_add_to_cache(vol->mft_ni); /* Can't use any of the higher level functions yet! */ l = ntfs_mst_pread(vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1, vol->mft_record_size, mb);