windows-nt/Source/XPSP1/NT/base/fs/dfs/driver/ipsup.c
2020-09-26 16:20:57 +08:00

778 lines
20 KiB
C

//+-------------------------------------------------------------------------
//
// Copyright (C) 1997, Microsoft Corporation.
//
// File: ipsup.c
//
// Contents: Support routines for managing DFS_IP_INFO entries and
// DFS_IP_PENDING_INFO entries
//
// Functions: DfsInitIp - Initialize the hash table for DFS_IP_INFO lookup
// DfsLookupIpInfo - Lookup a DFS_IP_INFO
// DfsAllocateIpInfo - Allocate a DFS_IP_INFO
// DfsInsertIpInfo - Put a DFS_IP_INFO into the table
// DfsDeleteIpInfo - Remove a DFS_IP_INFO from the table
// DfsReleaseIpInfo - Stop using a DFS_IP_INFO
//
// DfsFsctrlCreateIpInfo - Load an IpInfo table entry
// DfsFsctrlDeleteIpInfo - Remove an IpInfo table entry
//
// History: 16 Dec 1997 Jharper Created
//
//--------------------------------------------------------------------------
#include "dfsprocs.h"
#include "attach.h"
#include "ipsup.h"
#include "fsctrl.h"
#include "dfslpc.h"
#include "registry.h"
#include "regkeys.h"
#define Dbg 0x1000
//
// Manifest constants
//
#define IP_DEFAULT_HASH_SIZE 16 // default size of hash table
#define IP_DEFAULT_NUMBER_ENTRIES 250 // default max # of entries
#define IP_DEFAULT_TIMEOUT (60 * 60 * 24) // default time entry can live (in sec)
NTSTATUS
DfsInitIpInfoHashTable(
IN ULONG cHash,
OUT PIP_HASH_TABLE *ppHashTable
);
NTSTATUS
DfsAllocateIpInfo(
IN PDFS_IPADDRESS pDfsIpAddress,
IN PUNICODE_STRING pSiteName,
OUT PDFS_IP_INFO *ppIpInfo
);
VOID
DfsInsertIpInfo(
IN PDFS_IPADDRESS pDfsIpAddress,
IN PDFS_IP_INFO pIpInfo
);
VOID
DfsDeleteIpInfo(
PDFS_IP_INFO pIpInfo
);
ULONG
DfsHashIpAddress(
IN PDFS_IPADDRESS pDfsIpAddress,
IN DWORD HashMask
);
PDFS_IP_INFO
DfsLookupIpInfo(
IN PIP_HASH_TABLE pHashTable,
IN PDFS_IPADDRESS pDfsIpAddress
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DfsInitIp)
#pragma alloc_text(PAGE, DfsUninitIp)
#pragma alloc_text(PAGE, DfsInitIpInfoHashTable)
#pragma alloc_text(PAGE, DfsAllocateIpInfo)
#pragma alloc_text(PAGE, DfsLookupIpInfo)
#pragma alloc_text(PAGE, DfsInsertIpInfo)
#pragma alloc_text(PAGE, DfsDeleteIpInfo)
#pragma alloc_text(PAGE, DfsReleaseIpInfo)
#pragma alloc_text(PAGE, DfsHashIpAddress)
#pragma alloc_text(PAGE, DfsFsctrlCreateIpInfo)
#pragma alloc_text(PAGE, DfsFsctrlDeleteIpInfo)
#endif
#ifdef DBG
VOID
DfsDumpIpTable(void);
#endif
//+-------------------------------------------------------------------------
//
// Function: DfsInitIpHashTable - Initialize the DFS_IP_INFO lookup hash table
//
// Synopsis: This function initializes data structures which are
// used for looking up a DFS_IP_INFO associated with some IP address
//
// Arguments: [cHash] -- Size of the hash table to be allocated. Must be
// a power of two. If zero, a default size is used.
//
// Returns: NTSTATUS -- STATUS_SUCCESS, unless memory allocation
// fails.
//
// Note: The hash buckets are initialized to zero, then later
// initialized to a list head when used. This is a debugging
// aid to determine if some hash buckets are never used.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsInitIpHashTable(
ULONG cHash,
ULONG cEntries,
PIP_HASH_TABLE *ppHashTable)
{
PIP_HASH_TABLE pHashTable;
ULONG cbHashTable;
ULONG Timeout;
NTSTATUS status;
PBYTE pData;
if (cHash == 0) {
cHash = IP_DEFAULT_HASH_SIZE;
}
ASSERT ((cHash & (cHash-1)) == 0); // Assure cHash is a power of two
if (cEntries == 0) {
cEntries = IP_DEFAULT_NUMBER_ENTRIES;
}
cbHashTable = sizeof(IP_HASH_TABLE) + (cHash-1) * sizeof(LIST_ENTRY);
pHashTable = ExAllocatePoolWithTag(NonPagedPool, cbHashTable, ' sfD');
if (pHashTable == NULL) {
return STATUS_NO_MEMORY;
}
pHashTable->NodeTypeCode = DFS_NTC_IP_HASH;
pHashTable->NodeByteSize = (NODE_BYTE_SIZE) cbHashTable;
pHashTable->MaxEntries = cEntries;
pHashTable->EntryCount = 0;
InitializeListHead(&pHashTable->LRUChain);
pHashTable->HashMask = (cHash-1);
ExInitializeFastMutex( &pHashTable->HashListMutex );
RtlZeroMemory(&pHashTable->HashBuckets[0], cHash * sizeof(LIST_ENTRY));
//
// If there is a timeout override in the registry, get it
//
Timeout = IP_DEFAULT_TIMEOUT;
status = KRegSetRoot(wszRegDfsDriver);
if (NT_SUCCESS(status)) {
status = KRegGetValue(
L"",
wszIpCacheTimeout,
(PVOID ) &pData);
KRegCloseRoot();
if (NT_SUCCESS(status)) {
Timeout = *((ULONG*)pData);
ExFreePool(pData);
}
}
pHashTable->Timeout.QuadPart = UInt32x32To64(
Timeout,
10 * 1000 * 1000
);
*ppHashTable = pHashTable;
return(STATUS_SUCCESS);
}
NTSTATUS
DfsInitIp(
ULONG cHash,
ULONG cEntries)
{
NTSTATUS status;
status = DfsInitIpHashTable( cHash, cEntries, &DfsData.IpHashTable );
return status;
}
VOID
DfsUninitIp(
VOID
)
{
ExFreePool (DfsData.IpHashTable);
}
//+-------------------------------------------------------------------------
//
// Function: DfsLookupIpInfo - Lookup a DFS_IP_INFO in the hash table
//
// Synopsis: This function will lookup a DFS_IP_INFO.
// It will increment the UseCount on the DFS_IP_INFO.
//
// Arguments: [pDfsIpAddress] -- Ip address being looked up.
//
// Returns: PVOID -- pointer to the DFS_IP_INFO found, or NULL if none
//
//--------------------------------------------------------------------------
PDFS_IP_INFO
DfsLookupIpInfo(
PIP_HASH_TABLE pHashTable,
PDFS_IPADDRESS pDfsIpAddress)
{
PLIST_ENTRY pListHead, pLink;
PDFS_IP_INFO pIpInfo;
ExAcquireFastMutex( &pHashTable->HashListMutex);
pListHead = &pHashTable->HashBuckets[DfsHashIpAddress(pDfsIpAddress,pHashTable->HashMask)];
if ((pListHead->Flink == NULL) || // list not initialized
(pListHead->Flink == pListHead)) { // list empty
ExReleaseFastMutex( &pHashTable->HashListMutex );
return NULL;
}
for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) {
pIpInfo = CONTAINING_RECORD(pLink, DFS_IP_INFO, HashChain);
if (pDfsIpAddress->IpFamily == pIpInfo->IpAddress.IpFamily
&&
pDfsIpAddress->IpLen == pIpInfo->IpAddress.IpLen
&&
RtlCompareMemory(
pDfsIpAddress->IpData,
pIpInfo->IpAddress.IpData,
pDfsIpAddress->IpLen) == pDfsIpAddress->IpLen
) {
RemoveEntryList(&pIpInfo->LRUChain);
InsertHeadList(&pHashTable->LRUChain, &pIpInfo->LRUChain);
pIpInfo->UseCount++;
ExReleaseFastMutex( &pHashTable->HashListMutex );
return pIpInfo;
}
}
ExReleaseFastMutex( &pHashTable->HashListMutex);
return NULL;
}
//+-------------------------------------------------------------------------
//
// Function: DfsInsertIpInfo - Inserts a DFS_IP_INFO into the hash table
//
// Synopsis: This function associates a DFS_IP_INFO with an Ip address. This
// involves removing any existing entry, and adding the new.
//
// Arguments: [pDfsIpAddress] -- Pointer to the corresponding IpAddress, used
// as the hash key.
// [pIpInfo] -- Pointer to the DFS_IP_INFO to be inserted.
//
// Returns: -nothing-
//
//--------------------------------------------------------------------------
VOID
DfsInsertIpInfo(
PDFS_IPADDRESS pDfsIpAddress,
PDFS_IP_INFO pIpInfo)
{
PIP_HASH_TABLE pHashTable = (PIP_HASH_TABLE) DfsData.IpHashTable;
PLIST_ENTRY pListHead;
PDFS_IP_INFO pExistingIpInfo;
PDFS_IP_INFO pTailIpInfo;
LARGE_INTEGER now;
pExistingIpInfo = DfsLookupIpInfo(pHashTable, &pIpInfo->IpAddress);
//
// Put the new one in
//
ExAcquireFastMutex( &pHashTable->HashListMutex);
pListHead = &pHashTable->HashBuckets[DfsHashIpAddress(pDfsIpAddress,pHashTable->HashMask)];
if (pListHead->Flink == NULL) {
InitializeListHead(pListHead);
}
KeQuerySystemTime(&now);
pIpInfo->Timeout.QuadPart = now.QuadPart + pHashTable->Timeout.QuadPart;
InsertHeadList(pListHead, &pIpInfo->HashChain);
InsertHeadList(&pHashTable->LRUChain, &pIpInfo->LRUChain);
pHashTable->EntryCount++;
//
// If adding this entry causes the number of entries to exceed the maximum,
// then remove entries from the tail of the LRU list.
//
pListHead = &pHashTable->LRUChain;
if (pHashTable->EntryCount > pHashTable->MaxEntries && pListHead->Blink != pListHead) {
pTailIpInfo = CONTAINING_RECORD(pListHead->Blink, DFS_IP_INFO, LRUChain);
if (pTailIpInfo != pIpInfo) {
pTailIpInfo->Flags |= IP_INFO_DELETE_PENDING;
RemoveEntryList(&pTailIpInfo->HashChain);
RemoveEntryList(&pTailIpInfo->LRUChain);
if (pTailIpInfo->UseCount == 0) {
ExFreePool(pTailIpInfo);
}
pHashTable->EntryCount--;
}
}
ExReleaseFastMutex( &pHashTable->HashListMutex );
if (pExistingIpInfo != NULL) {
DfsDeleteIpInfo(
pExistingIpInfo);
DfsReleaseIpInfo(
pExistingIpInfo);
}
DebugTrace(0, Dbg, "Added pIpInfo %08lx ", pIpInfo);
DebugTrace(0, Dbg, "For Site %wZ ", &pIpInfo->SiteName);
}
//+-------------------------------------------------------------------------
//
// Function: DfsDeleteIpInfo - Delete a DFS_IP_INFO from the lookup hash table
//
// Synopsis: This function Deletes a DFS_IP_INFO from the hash table.
//
// Arguments: [pIpInfo] -- Pointer to the DFS_IP_INFO to delete
//
// Returns: -nothing-
//
//--------------------------------------------------------------------------
VOID
DfsDeleteIpInfo(
PDFS_IP_INFO pIpInfo)
{
PIP_HASH_TABLE pHashTable = (PIP_HASH_TABLE) DfsData.IpHashTable;
ExAcquireFastMutex( &pHashTable->HashListMutex);
pIpInfo->Flags |= IP_INFO_DELETE_PENDING;
RemoveEntryList(&pIpInfo->HashChain);
RemoveEntryList(&pIpInfo->LRUChain);
pHashTable->EntryCount--;
ExReleaseFastMutex( &pHashTable->HashListMutex);
DebugTrace(0, Dbg, "deleted pIpInfo %08lx ", pIpInfo);
DebugTrace(0, Dbg, "For site %wZ ", &pIpInfo->SiteName);
}
//+----------------------------------------------------------------------------
//
// Function: DfsAllocateIpInfo - Allocate a DFS_IP_INFO
//
// Synopsis: This function allocates a contiguous DFS_IP_INFO struct. The
// strings are stored in the allocated buffer after the DFS_IP_INFO
// structure.
//
// Arguments: [pSiteName] -- The site name
// [pDfsIpAddress -- The Ip address of the client
// [ppIpInfo] -- On successful return, has pointer to newly allocated
// DFS_IP_INFO.
//
// Returns: [STATUS_SUCCESS] -- Successfully allocated DFS_IP_INFO
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Out of memory condition
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsAllocateIpInfo(
PDFS_IPADDRESS pDfsIpAddress,
PUNICODE_STRING pSiteName,
PDFS_IP_INFO *ppIpInfo)
{
NTSTATUS status;
PDFS_IP_INFO pIpInfo;
ULONG Size;
ULONG i;
LPWSTR pwCh;
PUNICODE_STRING pustr;
DebugTrace(0, Dbg, "DfsAllocateIpInfo(%wZ)\n", pSiteName);
//
// Size the buffer - include storage for the unicode string after the
// DFS_IP_INFO structure.
//
Size = sizeof(DFS_IP_INFO) + pSiteName->Length;
pIpInfo = (PDFS_IP_INFO) ExAllocatePoolWithTag( PagedPool, Size, ' sfD' );
if (pIpInfo != NULL) {
RtlZeroMemory( pIpInfo, Size );
pIpInfo->NodeTypeCode = DFS_NTC_IP_INFO;
pIpInfo->NodeByteSize = (USHORT)Size;
pIpInfo->IpAddress = *pDfsIpAddress;
pwCh = (LPWSTR) &pIpInfo[1];
pustr = &pIpInfo->SiteName;
pustr->Length = pustr->MaximumLength = pSiteName->Length;
pustr->Buffer = pwCh;
RtlCopyMemory(pwCh, pSiteName->Buffer, pSiteName->Length);
pwCh += pustr->Length / sizeof(WCHAR);
*ppIpInfo = pIpInfo;
status = STATUS_SUCCESS;
DebugTrace(0, Dbg, "DfsAllocateIpInfo pIpInfo = %d\n", pIpInfo);
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: DfsReleaseIpInfo
//
// Synopsis: Decrements UseCount of and possibly frees a DFS_IP_INFO
//
// Arguments: [pIpInfo] -- The DFS_IP_INFO to release
//
// Returns: Nothing
//
//-----------------------------------------------------------------------------
VOID
DfsReleaseIpInfo(
PDFS_IP_INFO pIpInfo)
{
PIP_HASH_TABLE pHashTable = (PIP_HASH_TABLE) DfsData.IpHashTable;
if (pIpInfo == NULL) {
return;
}
//
// There's a potential race with DfsDeleteIpInfo/DfsInsertIpInfo w.r.t.
// DELETE_PENDING and the test below of DELETE_PENDING, so we still have
// to acquire the Mutex to safely test the DELETE_PENDING bit.
//
ExAcquireFastMutex( &pHashTable->HashListMutex);
pIpInfo->UseCount--;
if ((pIpInfo->Flags & IP_INFO_DELETE_PENDING) != 0 && pIpInfo->UseCount == 0) {
ExFreePool(pIpInfo);
}
ExReleaseFastMutex( &pHashTable->HashListMutex);
}
//+----------------------------------------------------------------------------
//
// Function: DfsHashIpAddress
//
// Synopsis: Generates a hash 0-N
//
// Arguments: [pDfsIpAddress] -- Ip address to hash
//
// Returns: Nothing
//
//-----------------------------------------------------------------------------
ULONG
DfsHashIpAddress(
PDFS_IPADDRESS pDfsAddress,
DWORD HashMask)
{
ULONG BucketNo = 0;
CHAR *pBuffer = pDfsAddress->IpData;
CHAR *pBufferEnd = &pBuffer[pDfsAddress->IpLen];
ULONG Ch;
BucketNo = 0;
while (pBuffer != pBufferEnd) {
Ch = *pBuffer & 0xff;
BucketNo *= 131;
BucketNo += Ch;
pBuffer++;
}
BucketNo = BucketNo & HashMask;
return BucketNo;
}
//+-------------------------------------------------------------------------
//
// Function: DfsFsctrlCreateIpInfo, public
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
//--------------------------------------------------------------------------
NTSTATUS
DfsFsctrlCreateIpInfo(
PIRP Irp,
PVOID InputBuffer,
ULONG InputBufferLength)
{
NTSTATUS status = STATUS_SUCCESS;
PDFS_CREATE_IP_INFO_ARG arg;
PDFS_IP_INFO pIpInfo;
ULONG i;
DebugTrace(+1, Dbg, "DfsFsctrlCreateIpInfo()\n", 0);
STD_FSCTRL_PROLOGUE(DfsFsctrlCreateIpInfo, TRUE, FALSE);
if (InputBufferLength < sizeof(DFS_CREATE_IP_INFO_ARG)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
//
// unmarshal the arguments...
//
arg = (PDFS_CREATE_IP_INFO_ARG) InputBuffer;
OFFSET_TO_POINTER(arg->SiteName.Buffer, arg);
if (!UNICODESTRING_IS_VALID(arg->SiteName, InputBuffer, InputBufferLength)
||
arg->IpAddress.IpLen > sizeof(arg->IpAddress.IpData)
) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
status = DfsAllocateIpInfo(
&arg->IpAddress,
&arg->SiteName,
&pIpInfo);
if (NT_SUCCESS(status)) {
DfsInsertIpInfo(
&arg->IpAddress,
pIpInfo);
}
exit_with_status:
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg,
"DfsFsctrlCreateIpInfo: Exit -> %08lx\n", ULongToPtr( status ) );
return status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsFsctrlDeleteIpInfo, public
//
// Synopsis:
//
// Arguments:
//
// Returns:
//
//--------------------------------------------------------------------------
NTSTATUS
DfsFsctrlDeleteIpInfo(
PIRP Irp,
PVOID InputBuffer,
ULONG InputBufferLength)
{
NTSTATUS status = STATUS_SUCCESS;
PDFS_DELETE_IP_INFO_ARG arg;
PDFS_IP_INFO pIpInfo;
PIP_HASH_TABLE pHashTable = DfsData.IpHashTable;
DebugTrace(+1, Dbg, "DfsFsctrlDeleteIpInfo()\n", 0);
STD_FSCTRL_PROLOGUE(DfsFsctrlDeleteIpInfo, TRUE, FALSE);
if (InputBufferLength < sizeof(DFS_DELETE_IP_INFO_ARG)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
//
// unmarshal the arguments...
//
arg = (PDFS_DELETE_IP_INFO_ARG) InputBuffer;
if ( arg->IpAddress.IpLen > sizeof(arg->IpAddress.IpData)) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_status;
}
pIpInfo = DfsLookupIpInfo(
pHashTable,
&arg->IpAddress);
//
// The DfsLookupIpInfo() call bumped the usecount, so we're sure pIpInfo
// won't become invalid as we're using it.
//
if (pIpInfo != NULL) {
//
// Removes from the table, but doesn't free the memory
//
DfsDeleteIpInfo(
pIpInfo);
//
// This will decrement the usecount, and if it goes to zero, frees the memory
//
DfsReleaseIpInfo(
pIpInfo);
}
exit_with_status:
DfsCompleteRequest( Irp, status );
DebugTrace(-1, Dbg,
"DfsFsctrlDeleteIpInfo: Exit -> %08lx\n", ULongToPtr( status ) );
return status;
}
PDFS_IP_INFO
DfsLookupSiteByIpaddress(
PDFS_IPADDRESS pDfsIpAddress,
BOOLEAN UseForce)
{
PDFS_IP_INFO pIpInfo;
LARGE_INTEGER now;
NTSTATUS status;
PIP_HASH_TABLE pHashTable = DfsData.IpHashTable;
if (pDfsIpAddress == NULL) {
return NULL;
}
KeQuerySystemTime(&now);
pIpInfo = DfsLookupIpInfo(pHashTable, pDfsIpAddress);
if (pIpInfo == NULL || now.QuadPart > pIpInfo->Timeout.QuadPart) {
//
// Entry is not in cache, or is old
//
if (pIpInfo != NULL) {
// Old entry - try for a new one
if (UseForce == TRUE) {
ExAcquireFastMutex( &pHashTable->HashListMutex);
pIpInfo->Timeout.QuadPart = now.QuadPart + UInt32x32To64(
10 * 60,
10 * 1000 * 1000);
ExReleaseFastMutex( &pHashTable->HashListMutex);
DfsLpcIpRequest(pDfsIpAddress);
pIpInfo = DfsLookupIpInfo(pHashTable, pDfsIpAddress);
}
} else {
if (UseForce == TRUE) {
DfsLpcIpRequest(pDfsIpAddress);
pIpInfo = DfsLookupIpInfo(pHashTable, pDfsIpAddress);
}
}
}
return pIpInfo;
}
#ifdef DBG
VOID
DfsDumpIpTable(void)
{
PLIST_ENTRY pListHead, pLink;
PDFS_IP_INFO pIpInfo;
PIP_HASH_TABLE pHashTable = DfsData.IpHashTable;
ULONG i, j;
DbgPrint("%d entries total\n", pHashTable->EntryCount);
for (i = 0; i <= pHashTable->HashMask; i++) {
pListHead = &pHashTable->HashBuckets[i];
if ((pListHead->Flink == NULL) || // list not initialized
(pListHead->Flink == pListHead)) { // list empty
continue;
}
for (pLink = pListHead->Flink; pLink != pListHead; pLink = pLink->Flink) {
pIpInfo = CONTAINING_RECORD(pLink, DFS_IP_INFO, HashChain);
DbgPrint("B:%02d Ip:0x%x N:%wZ C=%d\n",
i,
*((ULONG *)&pIpInfo->IpAddress.IpData),
&pIpInfo->SiteName,
pIpInfo->UseCount);
}
}
}
#endif