diff --git a/include/ntfs/attrib.h b/include/ntfs/attrib.h index bb911338..b20e0ffd 100644 --- a/include/ntfs/attrib.h +++ b/include/ntfs/attrib.h @@ -27,6 +27,7 @@ typedef struct _ntfs_attr ntfs_attr; typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx; +#include "list.h" #include "types.h" #include "inode.h" #include "unistr.h" @@ -195,6 +196,8 @@ struct _ntfs_attr { u8 compression_block_size_bits; u8 compression_block_clusters; ntfs_crypto_attr *crypto; + struct list_head list_entry; + int nr_references; }; /** diff --git a/include/ntfs/inode.h b/include/ntfs/inode.h index 0e1b0f2d..8fb907ab 100644 --- a/include/ntfs/inode.h +++ b/include/ntfs/inode.h @@ -161,6 +161,8 @@ struct _ntfs_inode { int nr_references; /* How many times this inode was opened. We really close inode only when this reaches zero. */ + + struct list_head attr_cache; /* List of opened attributes. */ }; extern void __ntfs_inode_add_to_cache(ntfs_inode *ni); diff --git a/libntfs/attrib.c b/libntfs/attrib.c index aae2e123..a1fe9545 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -354,6 +354,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfs_attr_search_ctx *ctx; ntfs_attr *na; ATTR_RECORD *a; + struct list_head *pos; int err; BOOL cs; @@ -363,6 +364,21 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, errno = EINVAL; return NULL; } + /* Check cache, maybe this attribute already opened? */ + list_for_each(pos, &ni->attr_cache) { + ntfs_attr *tmp_na; + + tmp_na = list_entry(pos, ntfs_attr, list_entry); + if (tmp_na->type == type && tmp_na->name_len == name_len && + !ntfs_ucsncmp(tmp_na->name, name, name_len)) { + ntfs_log_trace("Found this attribute in cache, " + "increment reference count and " + "return it.\n"); + tmp_na->nr_references++; + return tmp_na; + } + } + /* Search failed. Properly open attrbute. */ na = calloc(sizeof(ntfs_attr), 1); if (!na) return NULL; @@ -429,6 +445,8 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfs_attr_put_search_ctx(ctx); if (NAttrEncrypted(na)) ntfs_crypto_attr_open(na); + list_add_tail(&na->list_entry, &ni->attr_cache); + na->nr_references = 1; return na; put_err_out: ntfs_attr_put_search_ctx(ctx); @@ -449,6 +467,14 @@ void ntfs_attr_close(ntfs_attr *na) { if (!na) return; + na->nr_references--; + if (na->nr_references) { + ntfs_log_trace("There are %d more references left to " + "this attribute.\n", na->nr_references); + return; + } + ntfs_log_trace("There are no more references left to this attribute\n"); + list_del(&na->list_entry); if (NAttrEncrypted(na)) ntfs_crypto_attr_close(na); if (NAttrNonResident(na) && na->rl) diff --git a/libntfs/inode.c b/libntfs/inode.c index b21404cc..712cfd13 100644 --- a/libntfs/inode.c +++ b/libntfs/inode.c @@ -62,8 +62,10 @@ static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) ntfs_inode *ni; ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode)); - if (ni) + if (ni) { ni->vol = vol; + INIT_LIST_HEAD(&ni->attr_cache); + } return ni; } @@ -292,6 +294,11 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_log_trace("There are no more references left to " "this inode.\n"); } + /* Check whether all attributes of this inode are closed. */ + if (!list_empty(&ni->attr_cache)) + ntfs_log_error("%s(): Not all attributes are closed. " + "We definitely have memory leak. " + "Continue anyway.\n", __FUNCTION__); /* If we have dirty metadata, write it out. */ if (NInoDirty(ni) || NInoAttrListDirty(ni)) { if (ntfs_inode_sync(ni)) {