You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
767 lines
20 KiB
767 lines
20 KiB
/*++
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|