767 lines
20 KiB
C
767 lines
20 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
namcache.c
|
||
|
||
Abstract:
|
||
|
||
The following functions are provided to support name cache management in
|
||
mini-rdrs. See namcache.h for a more complete description of how a mini-rdr
|
||
could use name caches to help eliminate trips to the server.
|
||
|
||
|
||
Author:
|
||
|
||
David Orbits [davidor] 9-Sep-1996
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
|
||
#include "prefix.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, RxNameCacheInitialize)
|
||
#pragma alloc_text(PAGE, RxNameCacheCreateEntry)
|
||
#pragma alloc_text(PAGE, RxNameCacheFetchEntry)
|
||
#pragma alloc_text(PAGE, RxNameCacheCheckEntry)
|
||
#pragma alloc_text(PAGE, RxNameCacheActivateEntry)
|
||
#pragma alloc_text(PAGE, RxNameCacheExpireEntry)
|
||
#pragma alloc_text(PAGE, RxNameCacheFreeEntry)
|
||
#pragma alloc_text(PAGE, RxNameCacheFinalize)
|
||
#pragma alloc_text(PAGE, RxNameCacheExpireEntryWithShortName)
|
||
#endif
|
||
|
||
#define Dbg (DEBUG_TRACE_NAMECACHE)
|
||
|
||
|
||
VOID
|
||
RxNameCacheInitialize(
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl,
|
||
IN ULONG MRxNameCacheSize,
|
||
IN ULONG MaximumEntries
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes a NAME_CACHE structure.
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL from which to
|
||
allocate the entry.
|
||
|
||
MRxNameCacheSize - The size in bytes of the mini-rdr portion of the name
|
||
cache entry.
|
||
|
||
MaximumEntries - The maximum number of entries that will ever be
|
||
allocated. E.g. This prevents an errant program which
|
||
opens tons of files with bad names from chewing up
|
||
paged pool.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
ExInitializeFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
InitializeListHead(&NameCacheCtl->ActiveList);
|
||
InitializeListHead(&NameCacheCtl->FreeList);
|
||
|
||
NameCacheCtl->NumberActivates = 0;
|
||
NameCacheCtl->NumberChecks = 0;
|
||
NameCacheCtl->NumberNameHits = 0;
|
||
NameCacheCtl->NumberNetOpsSaved = 0;
|
||
NameCacheCtl->EntryCount = 0;
|
||
NameCacheCtl->MaximumEntries = MaximumEntries;
|
||
NameCacheCtl->MRxNameCacheSize = MRxNameCacheSize;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
PNAME_CACHE
|
||
RxNameCacheCreateEntry (
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl,
|
||
IN PUNICODE_STRING Name,
|
||
IN BOOLEAN CaseInsensitive
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates and initializes a NAME_CACHE structure with the
|
||
given name string, Lifetime (in seconds) and MRxContext.
|
||
It returns a pointer to the name cache structure or NULL if no entry was
|
||
available. It is expected that the caller will then initialize any
|
||
additional mini-rdr portion of the name cache context and then put the
|
||
entry on the name cache active list by calling RxNameCacheActivateEntry().
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL from which to
|
||
allocate the entry.
|
||
|
||
Name - A pointer to the unicode name string to initialize the
|
||
the entry with.
|
||
|
||
CaseInsensitive - True if need case insensitive compare on name.
|
||
|
||
Return Value:
|
||
|
||
PNAME_CACHE - returns a pointer to the newly allocated NAME_CACHE struct
|
||
or NULL if allocation fails.
|
||
|
||
|
||
--*/
|
||
{
|
||
LONG i;
|
||
PNAME_CACHE *NameCacheArray;
|
||
PNAME_CACHE NameCache;
|
||
ULONG NameCacheSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( +1, Dbg, ("RxNameCacheCreateEntry: %wZ\n", Name ));
|
||
//
|
||
// Grab an entry off the free list.
|
||
//
|
||
|
||
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
if (!IsListEmpty(&NameCacheCtl->FreeList)) {
|
||
NameCache = (PNAME_CACHE) RemoveHeadList(&NameCacheCtl->FreeList);
|
||
} else {
|
||
NameCache = NULL;
|
||
}
|
||
|
||
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
if (NameCache != NULL) {
|
||
NameCache = CONTAINING_RECORD(NameCache, NAME_CACHE, Link);
|
||
RxDbgTrace(0, Dbg, ("took from free list\n"));
|
||
|
||
} else {
|
||
//
|
||
// Didn't get an entry off the free list, allocate one.
|
||
// Don't exceed Max but we could go over a little if multiple threads
|
||
// are allocating.
|
||
//
|
||
if (NameCacheCtl->EntryCount < NameCacheCtl->MaximumEntries) {
|
||
|
||
NameCacheSize = QuadAlign(sizeof(NAME_CACHE)) +
|
||
QuadAlign(NameCacheCtl->MRxNameCacheSize);
|
||
NameCache = RxAllocatePoolWithTag(
|
||
PagedPool,
|
||
NameCacheSize,
|
||
RX_NAME_CACHE_POOLTAG);
|
||
|
||
if (NameCache != NULL) {
|
||
//
|
||
// Init standard header fields, bump entry count & setup
|
||
// mini-rdr context extension.
|
||
//
|
||
ZeroAndInitializeNodeType(
|
||
NameCache,
|
||
RDBSS_NTC_STORAGE_TYPE_UNKNOWN,
|
||
(NODE_BYTE_SIZE) NameCacheSize);
|
||
|
||
InterlockedIncrement(&NameCacheCtl->EntryCount);
|
||
|
||
NameCache->Name.Buffer = NULL;
|
||
NameCache->Name.Length = 0;
|
||
NameCache->Name.MaximumLength = 0;
|
||
|
||
if (NameCacheCtl->MRxNameCacheSize > 0) {
|
||
NameCache->ContextExtension = (PBYTE)NameCache +
|
||
QuadAlign(sizeof(NAME_CACHE));
|
||
RxDbgTrace(0, Dbg, ("allocated new entry\n"));
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If still no entry then bag it.
|
||
//
|
||
if (NameCache == NULL) {
|
||
RxDbgTrace(-1, Dbg, ("Fail no entry allocated!\n"));
|
||
return NULL;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If name won't fit in current string, free it and allocate new string.
|
||
//
|
||
if (Name->Length > NameCache->Name.MaximumLength) {
|
||
if (NameCache->Name.Buffer != NULL) {
|
||
RxFreePool(NameCache->Name.Buffer);
|
||
}
|
||
|
||
if (Name->Length > 0) {
|
||
NameCache->Name.Buffer = RxAllocatePoolWithTag(
|
||
PagedPool,
|
||
(ULONG) Name->Length,
|
||
RX_NAME_CACHE_POOLTAG);
|
||
|
||
} else {
|
||
NameCache->Name.Buffer = NULL;
|
||
}
|
||
|
||
if (Name->Length > 0 &&
|
||
NameCache->Name.Buffer == NULL) {
|
||
//
|
||
// if didn't get the storage. Zero the string length and put entry
|
||
// back on the free list. Otherwise save allocation in max length.
|
||
//
|
||
NameCache->Name.Length = 0;
|
||
NameCache->Name.MaximumLength = 0;
|
||
|
||
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
|
||
InsertHeadList(&NameCacheCtl->FreeList, &NameCache->Link);
|
||
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
RxDbgTrace(-1, Dbg, ("Fail no pool for name!\n"));
|
||
return NULL;
|
||
} else {
|
||
NameCache->Name.MaximumLength = Name->Length;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Save the name & length. Set the case matching flag. Set the hash field.
|
||
//
|
||
NameCache->Name.Length = Name->Length;
|
||
NameCache->CaseInsensitive = CaseInsensitive;
|
||
|
||
if (Name->Length > 0) {
|
||
RtlMoveMemory(NameCache->Name.Buffer, Name->Buffer, Name->Length);
|
||
NameCache->HashValue = RxTableComputeHashValue(&NameCache->Name);
|
||
}else {
|
||
NameCache->HashValue = 0;
|
||
}
|
||
|
||
RxDbgTrace(-1, Dbg, ("Success!\n"));
|
||
return NameCache;
|
||
|
||
}
|
||
|
||
|
||
PNAME_CACHE
|
||
RxNameCacheFetchEntry (
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl,
|
||
IN PUNICODE_STRING Name
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks for a match in the name cache for Name.
|
||
If found the entry is removed from the Name Cache active list and
|
||
a pointer to the NAME_CACHE struct is returned. Otherwise NULL is returned.
|
||
The entry is removed to avoid problems with another thread trying to
|
||
update the same entry or observing that it expired and putting it on the
|
||
free list. We could get multiple entries with the same name by different
|
||
threads but eventually they will expire.
|
||
|
||
If a matching entry is found no check is made for expiration. That is left
|
||
to the caller since it is likely the caller would want to take a special
|
||
action.
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL to scan.
|
||
|
||
Name - A pointer to the unicode name string to scan for.
|
||
|
||
Return Value:
|
||
|
||
PNAME_CACHE - returns a pointer to the NAME_CACHE struct if found or NULL.
|
||
|
||
Side Effects:
|
||
|
||
As the active list is scanned any non-matching entries that have expired are
|
||
put on the free list.
|
||
|
||
--*/
|
||
{
|
||
PNAME_CACHE NameCache = NULL;
|
||
PLIST_ENTRY pListEntry;
|
||
PLIST_ENTRY ExpiredEntry;
|
||
ULONG HashValue;
|
||
LARGE_INTEGER CurrentTime;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Lookup %wZ\n", Name ));
|
||
|
||
if (Name->Length > 0) {
|
||
HashValue = RxTableComputeHashValue(Name);
|
||
} else {
|
||
HashValue = 0;
|
||
}
|
||
|
||
KeQueryTickCount( &CurrentTime );
|
||
|
||
NameCacheCtl->NumberChecks += 1;
|
||
//
|
||
// Get the lock and scan the active list.
|
||
//
|
||
|
||
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
pListEntry = NameCacheCtl->ActiveList.Flink;
|
||
|
||
while (pListEntry != &NameCacheCtl->ActiveList) {
|
||
|
||
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
|
||
//
|
||
// Do initial match on the hash value and the length. Then do full string.
|
||
//
|
||
if ((NameCache->HashValue == HashValue) &&
|
||
(Name->Length == NameCache->Name.Length)) {
|
||
|
||
if (Name->Length == 0 ||
|
||
RtlEqualUnicodeString(
|
||
Name,
|
||
&NameCache->Name,
|
||
NameCache->CaseInsensitive) ) {
|
||
//
|
||
// Found a match.
|
||
//
|
||
NameCacheCtl->NumberNameHits += 1;
|
||
break;
|
||
}
|
||
}
|
||
//
|
||
// No match. If the entry is expired, put it on the free list.
|
||
//
|
||
ExpiredEntry = pListEntry;
|
||
pListEntry = pListEntry->Flink;
|
||
|
||
if (CurrentTime.QuadPart >= NameCache->ExpireTime.QuadPart) {
|
||
RemoveEntryList(ExpiredEntry);
|
||
InsertHeadList(&NameCacheCtl->FreeList, ExpiredEntry);
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Entry expired %wZ\n", &NameCache->Name ));
|
||
}
|
||
|
||
NameCache = NULL;
|
||
}
|
||
//
|
||
// If we found something pull it off the active list and give it to caller.
|
||
//
|
||
if (NameCache != NULL) {
|
||
RemoveEntryList(pListEntry);
|
||
}
|
||
|
||
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
|
||
if (NameCache != NULL) {
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Entry found %wZ\n", &NameCache->Name ));
|
||
}
|
||
|
||
return NameCache;
|
||
|
||
}
|
||
|
||
|
||
RX_NC_CHECK_STATUS
|
||
RxNameCacheCheckEntry (
|
||
IN PNAME_CACHE NameCache,
|
||
IN ULONG MRxContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks a name cache entry for validity. A valid entry
|
||
means that the lifetime has not expired and the MRxContext passes
|
||
the equality check.
|
||
|
||
Arguments:
|
||
|
||
NameCache - pointer to NAME_CACHE struct to check.
|
||
|
||
MRxContext - A ULONG worth of mini-rdr supplied context for
|
||
equality checking when making a valid entry check.
|
||
|
||
Return Value:
|
||
|
||
RX_NC_CHECK_STATUS: RX_NC_SUCCESS - The entry is valid
|
||
RX_NC_TIME_EXPIRED - The Lifetime on the entry expired
|
||
RX_NC_MRXCTX_FAIL - The MRxContext equality test failed
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
LARGE_INTEGER CurrentTime;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check for Mini-rdr context equality.
|
||
//
|
||
if (NameCache->Context != MRxContext) {
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: MRxContext_Fail %08lx,%08lx %wZ\n",
|
||
NameCache->Context,
|
||
MRxContext,
|
||
&NameCache->Name ));
|
||
|
||
return RX_NC_MRXCTX_FAIL;
|
||
}
|
||
|
||
//
|
||
// Check for lifetime expired.
|
||
//
|
||
KeQueryTickCount( &CurrentTime );
|
||
if (CurrentTime.QuadPart >= NameCache->ExpireTime.QuadPart) {
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: Expired %wZ\n", &NameCache->Name ));
|
||
return RX_NC_TIME_EXPIRED;
|
||
}
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: Success %wZ\n", &NameCache->Name ));
|
||
return RX_NC_SUCCESS;
|
||
|
||
}
|
||
|
||
VOID
|
||
RxNameCacheExpireEntry(
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl,
|
||
IN PNAME_CACHE NameCache
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine puts the entry on the free list.
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL on which to
|
||
activate the entry.
|
||
|
||
NameCache - pointer to NAME_CACHE struct to activate.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Assumes:
|
||
|
||
The name cache entry is not on either the free or active list.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheExpireEntry: %wZ\n", &NameCache->Name ));
|
||
|
||
//
|
||
// Put the entry on free list for recycle.
|
||
//
|
||
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
|
||
InsertHeadList(&NameCacheCtl->FreeList, &NameCache->Link);
|
||
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
//
|
||
// Update stats.
|
||
//
|
||
NameCacheCtl->NumberActivates -= 1;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
RxNameCacheExpireEntryWithShortName (
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl,
|
||
IN PUNICODE_STRING Name
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine expires all the name cache whose name prefix matches the given short file name.
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL to scan.
|
||
|
||
Name - A pointer to the unicode name string to scan for.
|
||
|
||
Return Value:
|
||
|
||
PNAME_CACHE - returns a pointer to the NAME_CACHE struct if found or NULL.
|
||
|
||
Side Effects:
|
||
|
||
As the active list is scanned any non-matching entries that have expired are
|
||
put on the free list.
|
||
|
||
--*/
|
||
{
|
||
PNAME_CACHE NameCache = NULL;
|
||
PLIST_ENTRY pListEntry;
|
||
PLIST_ENTRY ExpiredEntry;
|
||
ULONG HashValue;
|
||
LARGE_INTEGER CurrentTime;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Lookup %wZ\n", Name ));
|
||
|
||
KeQueryTickCount( &CurrentTime );
|
||
|
||
NameCacheCtl->NumberChecks += 1;
|
||
//
|
||
// Get the lock and scan the active list.
|
||
//
|
||
|
||
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
pListEntry = NameCacheCtl->ActiveList.Flink;
|
||
|
||
while (pListEntry != &NameCacheCtl->ActiveList) {
|
||
USHORT SavedNameLength;
|
||
|
||
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
|
||
|
||
ExpiredEntry = pListEntry;
|
||
pListEntry = pListEntry->Flink;
|
||
|
||
//
|
||
// Do initial match on the hash value and the length. Then do full string.
|
||
//
|
||
if (Name->Length <= NameCache->Name.Length) {
|
||
SavedNameLength = NameCache->Name.Length;
|
||
NameCache->Name.Length = Name->Length;
|
||
|
||
if (Name->Length == 0 ||
|
||
RtlEqualUnicodeString(
|
||
Name,
|
||
&NameCache->Name,
|
||
NameCache->CaseInsensitive) ) {
|
||
//
|
||
// Found a match.
|
||
//
|
||
RemoveEntryList(ExpiredEntry);
|
||
InsertHeadList(&NameCacheCtl->FreeList, ExpiredEntry);
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheExpireEntryWithShortName: Entry expired %wZ\n", &NameCache->Name ));
|
||
|
||
continue;
|
||
}
|
||
|
||
NameCache->Name.Length = SavedNameLength;
|
||
}
|
||
}
|
||
|
||
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
|
||
}
|
||
|
||
VOID
|
||
RxNameCacheActivateEntry (
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl,
|
||
IN PNAME_CACHE NameCache,
|
||
IN ULONG LifeTime,
|
||
IN ULONG MRxContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a name cache entry and updates the expiration time and
|
||
the mini rdr context. It then puts the entry on the active list.
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL on which to
|
||
activate the entry.
|
||
|
||
NameCache - pointer to NAME_CACHE struct to activate.
|
||
|
||
LifeTime - The valid lifetime of the cache entry (in seconds).
|
||
A lifetime of zero means leave current value unchanged.
|
||
This is for reactivations after a match where you
|
||
want the original lifetime preserved.
|
||
|
||
MRxContext - A ULONG worth of mini-rdr supplied context for
|
||
equality checking when making a valid entry check.
|
||
An MRxContext of zero means leave current value unchanged.
|
||
This is for reactivations after a match where you
|
||
want the original MRxContext preserved.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Assumes:
|
||
|
||
The name cache entry is not on either the free or active list.
|
||
|
||
--*/
|
||
{
|
||
LARGE_INTEGER CurrentTime;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheActivateEntry: %wZ\n", &NameCache->Name ));
|
||
//
|
||
// Set new expiration time on the entry and save the mini-rdr context.
|
||
// A lifetime of zero or a MRxContext of zero implies leave value unchanged.
|
||
//
|
||
if (LifeTime != 0) {
|
||
KeQueryTickCount( &CurrentTime );
|
||
NameCache->ExpireTime.QuadPart = CurrentTime.QuadPart +
|
||
(LONGLONG) ((LifeTime * 10*1000*1000) / KeQueryTimeIncrement());
|
||
}
|
||
|
||
if (MRxContext != 0) {
|
||
NameCache->Context = MRxContext;
|
||
}
|
||
|
||
//
|
||
// Put the entry on active list.
|
||
//
|
||
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
|
||
InsertHeadList(&NameCacheCtl->ActiveList, &NameCache->Link);
|
||
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
//
|
||
// Update stats.
|
||
//
|
||
NameCacheCtl->NumberActivates += 1;
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RxNameCacheFreeEntry (
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl,
|
||
IN PNAME_CACHE NameCache
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases the storage for a name cache entry and decrements the
|
||
count of name cache entries for this name cache.
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL for the name cache.
|
||
|
||
NameCache - pointer to the NAME_CACHE struct to free.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Assumes:
|
||
|
||
The name cache entry is not on either the free or active list.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("RxNameCacheFreeEntry: %wZ\n", &NameCache->Name ));
|
||
//
|
||
// Release storage for name
|
||
//
|
||
if (NameCache->Name.Buffer != NULL) {
|
||
RxFreePool(NameCache->Name.Buffer);
|
||
}
|
||
//
|
||
// Release storage for NAME_CACHE entry (includes context ext., if any)
|
||
//
|
||
RxFreePool(NameCache);
|
||
|
||
InterlockedDecrement(&NameCacheCtl->EntryCount);
|
||
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RxNameCacheFinalize (
|
||
IN PNAME_CACHE_CONTROL NameCacheCtl
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases the storage for all the name cache entries.
|
||
|
||
Arguments:
|
||
|
||
NameCacheCtl - pointer to the NAME_CACHE_CONTROL for the name cache.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
{
|
||
PNAME_CACHE NameCache;
|
||
PLIST_ENTRY pListEntry;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the lock and remove entries from the active list.
|
||
//
|
||
|
||
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
|
||
while (!IsListEmpty(&NameCacheCtl->ActiveList)) {
|
||
|
||
pListEntry = RemoveHeadList(&NameCacheCtl->ActiveList);
|
||
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
|
||
|
||
RxNameCacheFreeEntry(NameCacheCtl, NameCache);
|
||
}
|
||
//
|
||
// scan free list and remove entries.
|
||
//
|
||
while (!IsListEmpty(&NameCacheCtl->FreeList)) {
|
||
|
||
pListEntry = RemoveHeadList(&NameCacheCtl->FreeList);
|
||
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
|
||
|
||
RxNameCacheFreeEntry(NameCacheCtl, NameCache);
|
||
}
|
||
|
||
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
|
||
|
||
//
|
||
// At this point the entry count should be zero. If not then there is
|
||
// a memory leak since someone didn't call free.
|
||
//
|
||
ASSERT(NameCacheCtl->EntryCount == 0);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|