1250 lines
32 KiB
C
1250 lines
32 KiB
C
//+----------------------------------------------------------------------------
|
||
//
|
||
// Copyright (C) 1992, Microsoft Corporation.
|
||
//
|
||
// File: PKTFSCTL.C
|
||
//
|
||
// Contents: This module contains the implementation for FS controls
|
||
// which manipulate the PKT.
|
||
//
|
||
// Functions: PktFsctrlUpdateDomainKnowledge -
|
||
// PktFsctrlGetRelationInfo -
|
||
// PktFsctrlSetRelationInfo -
|
||
// PktFsctrlIsChildnameLegal -
|
||
// PktFsctrlCreateEntry -
|
||
// PktFsctrlCreateSubordinateEntry -
|
||
// PktFsctrlDestroyEntry -
|
||
// PktFsctrlUpdateSiteCosts -
|
||
// DfsFsctrlSetDCName -
|
||
// DfsAgePktEntries - Flush PKT entries periodically
|
||
//
|
||
// Private Functions
|
||
//
|
||
// DfsCreateExitPathOnRoot
|
||
// PktpHashSiteCostList
|
||
// PktpLookupSiteCost
|
||
// PktpUpdateSiteCosts
|
||
// PktpSetActiveSpcService
|
||
//
|
||
// Debug Only Functions
|
||
//
|
||
// PktFsctrlFlushCache - Flush PKT entries on command
|
||
// PktFsctrlFlushSpcCache - Flush SPC entries on command
|
||
// PktFsctrlGetFirstSvc - Test hooks for testing replica
|
||
// PktFsctrlGetNextSvc - selection.
|
||
//
|
||
// History: 12 Jul 1993 Alanw Created from localvol.c.
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
#include "dfsprocs.h"
|
||
#include "dfserr.h"
|
||
#include "fsctrl.h"
|
||
#include "log.h"
|
||
#include "dnr.h"
|
||
#include "know.h"
|
||
|
||
#include <stdlib.h>
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_LOCALVOL)
|
||
|
||
//
|
||
// Local function prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
DfspProtocolToService(
|
||
IN PDS_TRANSPORT pdsTransport,
|
||
IN PWSTR pwszPrincipalName,
|
||
IN PWSTR pwszShareName,
|
||
IN BOOLEAN fIsDfs,
|
||
IN OUT PDFS_SERVICE pService);
|
||
|
||
NTSTATUS
|
||
PktFsctrlFlushCache(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
);
|
||
|
||
NTSTATUS
|
||
PktFsctrlFlushSpcCache(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
);
|
||
|
||
VOID
|
||
PktFlushChildren(
|
||
PDFS_PKT_ENTRY pEntry
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, DfsAgePktEntries )
|
||
#pragma alloc_text( PAGE, DfspProtocolToService )
|
||
#pragma alloc_text( PAGE, DfsFsctrlSetDCName )
|
||
#pragma alloc_text( PAGE, PktpSetActiveSpcService )
|
||
#pragma alloc_text( PAGE, PktFlushChildren )
|
||
#pragma alloc_text( PAGE, PktFsctrlFlushCache )
|
||
#pragma alloc_text( PAGE, PktFsctrlFlushSpcCache )
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
|
||
//+----------------------------------------------------------------------
|
||
//
|
||
// Function: DfsAgePktEntries, public
|
||
//
|
||
// Synopsis: This function gets called in the FSP to step through the PKT
|
||
// entries and delete those entries which are old.
|
||
//
|
||
// Arguments: [TimerContext] -- This context block contains a busy flag
|
||
// and a count of the number of ticks that
|
||
// have elapsed.
|
||
//
|
||
// Returns: Nothing.
|
||
//
|
||
// Notes: In case the PKT cannot be acquired exclusive, the
|
||
// routine just returns without doing anything. We
|
||
// will have missed an aging interval, but aging is
|
||
// a non-critical activity.
|
||
//
|
||
// History: 04/23/93 SudK Created.
|
||
//
|
||
//-----------------------------------------------------------------------
|
||
VOID
|
||
DfsAgePktEntries(PDFS_TIMER_CONTEXT DfsTimerContext)
|
||
{
|
||
|
||
PDFS_PKT pkt = _GetPkt();
|
||
PDFS_PKT_ENTRY entry, nextEntry;
|
||
PDFS_SPECIAL_ENTRY sentry, snextEntry;
|
||
PLIST_ENTRY link;
|
||
PDFS_CREDENTIALS creds;
|
||
BOOLEAN pktLocked = FALSE;
|
||
PDFS_SPECIAL_TABLE pSpecialTable;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsAgePktEntries called\n", 0);
|
||
|
||
pSpecialTable = &pkt->SpecialTable;
|
||
|
||
//
|
||
// First we need to acquire a lock on the PKT and step through the PKT
|
||
//
|
||
//
|
||
|
||
// If we can't get to the resource then let us return right away.
|
||
// This is really not that critical. We can always try again.
|
||
//
|
||
|
||
PktAcquireExclusive(FALSE, &pktLocked);
|
||
|
||
if (pktLocked == FALSE) {
|
||
|
||
DfsTimerContext->TickCount = 0;
|
||
|
||
DfsTimerContext->InUse = FALSE;
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsAgePktEntries Exit (no scan)\n", 0);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
if (ExAcquireResourceExclusiveLite(&DfsData.Resource, FALSE) == FALSE) {
|
||
|
||
PktRelease();
|
||
|
||
DfsTimerContext->TickCount = 0;
|
||
|
||
DfsTimerContext->InUse = FALSE;
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsAgePktEntries Exit (no scan 2)\n", 0);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// Age all the Pkt entries
|
||
//
|
||
|
||
entry = PktFirstEntry(pkt);
|
||
|
||
while (entry != NULL) {
|
||
|
||
DfsDbgTrace(0, Dbg, "DfsAgePktEntries: Scanning %wZ\n", &entry->Id.Prefix);
|
||
|
||
nextEntry = PktNextEntry(pkt, entry);
|
||
|
||
if (entry->ExpireTime < DfsTimerContext->TickCount) {
|
||
#if DBG
|
||
if (MupVerbose)
|
||
DbgPrint("DfsAgePktEntries:Setting expiretime on %wZ to 0\n",
|
||
&entry->Id.Prefix);
|
||
#endif
|
||
entry->ExpireTime = 0;
|
||
} else {
|
||
entry->ExpireTime -= DfsTimerContext->TickCount;
|
||
}
|
||
|
||
entry = nextEntry;
|
||
|
||
}
|
||
|
||
//
|
||
// Age the special table
|
||
//
|
||
|
||
if (pkt->SpecialTable.SpecialEntryCount > 0) {
|
||
|
||
if (pkt->SpecialTable.TimeToLive >= DfsTimerContext->TickCount) {
|
||
|
||
pkt->SpecialTable.TimeToLive -= DfsTimerContext->TickCount;
|
||
|
||
} else { // make it zero
|
||
|
||
pkt->SpecialTable.TimeToLive = 0;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Check the deleted credentials queue...
|
||
//
|
||
|
||
for (link = DfsData.DeletedCredentials.Flink;
|
||
link != &DfsData.DeletedCredentials;
|
||
NOTHING) {
|
||
|
||
creds = CONTAINING_RECORD(link, DFS_CREDENTIALS, Link);
|
||
|
||
link = link->Flink;
|
||
|
||
if (creds->RefCount == 0) {
|
||
|
||
RemoveEntryList( &creds->Link );
|
||
|
||
ExFreePool( creds );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
ExReleaseResourceLite( &DfsData.Resource );
|
||
|
||
PktRelease();
|
||
|
||
//
|
||
// Finally we need to reset the count so that the Timer Routine can
|
||
// work fine. We also release the context block by resetting the InUse
|
||
// boolean. This will make sure that the next count towards the PKT
|
||
// aging will start again.
|
||
//
|
||
|
||
DfsTimerContext->TickCount = 0;
|
||
|
||
DfsTimerContext->InUse = FALSE;
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsAgePktEntries Exit\n", 0);
|
||
}
|
||
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfspProtocolToService
|
||
//
|
||
// Synopsis: Given a NetBIOS protocol definition in a DS_PROTOCOL structure
|
||
// this function creates a corresponding DFS_SERVICE structure.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfspProtocolToService(
|
||
IN PDS_TRANSPORT pdsTransport,
|
||
IN PWSTR pwszPrincipalName,
|
||
IN PWSTR pwszShareName,
|
||
IN BOOLEAN fIsDfs,
|
||
IN OUT PDFS_SERVICE pService)
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PTA_ADDRESS pTaddr = &pdsTransport->taddr;
|
||
PTDI_ADDRESS_NETBIOS pNBAddress;
|
||
USHORT i;
|
||
WCHAR NetBiosAddress[ TDI_ADDRESS_LENGTH_NETBIOS + 1];
|
||
ULONG cbUnused;
|
||
PUNICODE_STRING pServiceAddr;
|
||
ULONG AllocLen;
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfspProtocolToService - entered\n", 0);
|
||
|
||
//
|
||
// Initialize the service to nulls
|
||
//
|
||
|
||
RtlZeroMemory(pService, sizeof(DFS_SERVICE));
|
||
|
||
ASSERT(pTaddr->AddressType == TDI_ADDRESS_TYPE_NETBIOS);
|
||
|
||
pNBAddress = (PTDI_ADDRESS_NETBIOS) pTaddr->Address;
|
||
ASSERT(pTaddr->AddressLength == sizeof(TDI_ADDRESS_NETBIOS));
|
||
|
||
RtlMultiByteToUnicodeN(
|
||
NetBiosAddress,
|
||
sizeof(NetBiosAddress),
|
||
&cbUnused,
|
||
pNBAddress->NetbiosName,
|
||
16);
|
||
|
||
//
|
||
// Process a NetBIOS name. Throw away char 16, then ignore the trailing
|
||
// spaces
|
||
//
|
||
|
||
for (i = 14; i >= 0 && NetBiosAddress[i] == L' '; i--) {
|
||
NOTHING;
|
||
}
|
||
NetBiosAddress[i+1] = UNICODE_NULL;
|
||
|
||
DfsDbgTrace(0, Dbg, "NetBIOS address is %ws\n", NetBiosAddress);
|
||
|
||
pService->Name.Length = wcslen(pwszPrincipalName) * sizeof(WCHAR);
|
||
pService->Name.MaximumLength = pService->Name.Length +
|
||
sizeof(UNICODE_NULL);
|
||
pService->Name.Buffer = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
pService->Name.MaximumLength,
|
||
' puM');
|
||
|
||
if (!pService->Name.Buffer) {
|
||
DfsDbgTrace(0, Dbg, "Unable to create principal name!\n", 0);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DfsDbgTrace(-1, Dbg, "DfsProtocolToService returning %08lx\n", ULongToPtr(status) );
|
||
return(status);
|
||
}
|
||
|
||
RtlCopyMemory(pService->Name.Buffer, pwszPrincipalName, pService->Name.Length);
|
||
|
||
AllocLen = sizeof(UNICODE_PATH_SEP) +
|
||
pService->Name.Length +
|
||
sizeof(UNICODE_PATH_SEP) +
|
||
wcslen(pwszShareName) * sizeof(WCHAR) +
|
||
sizeof(UNICODE_NULL);
|
||
|
||
if (AllocLen <= MAXUSHORT) {
|
||
pService->Address.MaximumLength = (USHORT) AllocLen;
|
||
} else {
|
||
DfsDbgTrace(0, Dbg, "Address too long!\n", 0);
|
||
ExFreePool(pService->Name.Buffer);
|
||
status = STATUS_NAME_TOO_LONG;
|
||
DfsDbgTrace(-1, Dbg, "DfsProtocolToService returning %08lx\n", ULongToPtr(status) );
|
||
return(status);
|
||
}
|
||
|
||
pService->Address.Buffer = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
pService->Address.MaximumLength,
|
||
' puM');
|
||
|
||
if (!pService->Address.Buffer) {
|
||
DfsDbgTrace(0, Dbg, "Unable to create address!\n", 0);
|
||
ExFreePool(pService->Name.Buffer);
|
||
pService->Name.Buffer = NULL;
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DfsDbgTrace(-1, Dbg, "DfsProtocolToService returning %08lx\n", ULongToPtr(status) );
|
||
return(status);
|
||
}
|
||
|
||
pService->Address.Length = sizeof(UNICODE_PATH_SEP);
|
||
|
||
pService->Address.Buffer[0] = UNICODE_PATH_SEP;
|
||
|
||
DnrConcatenateFilePath(
|
||
&pService->Address,
|
||
pService->Name.Buffer,
|
||
pService->Name.Length);
|
||
|
||
DnrConcatenateFilePath(
|
||
&pService->Address,
|
||
pwszShareName,
|
||
(USHORT) (wcslen(pwszShareName) * sizeof(WCHAR)));
|
||
|
||
DfsDbgTrace(0, Dbg, "Server Name is %wZ\n", &pService->Name);
|
||
|
||
DfsDbgTrace(0, Dbg, "Address is %wZ\n", &pService->Address);
|
||
|
||
pService->Type = DFS_SERVICE_TYPE_MASTER;
|
||
|
||
if (fIsDfs) {
|
||
pService->Capability = PROV_DFS_RDR;
|
||
pService->ProviderId = PROV_ID_DFS_RDR;
|
||
} else {
|
||
pService->Capability = PROV_STRIP_PREFIX;
|
||
pService->ProviderId = PROV_ID_MUP_RDR;
|
||
}
|
||
pService->pProvider = NULL;
|
||
|
||
DfsDbgTrace(-1, Dbg, "DfsProtocolToService returning %08lx\n", ULongToPtr(status) );
|
||
return(status);
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsFsctrlSetDCName
|
||
//
|
||
// Synopsis: Sets the DC to use for special referrals,
|
||
// also tries for more referrals if the table is emty or old,
|
||
// and also sets the preferred DC if a new DC is passed in.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsFsctrlSetDCName(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PDFS_PKT Pkt = _GetPkt();
|
||
BOOLEAN GotPkt = FALSE;
|
||
BOOLEAN GotNewDc = FALSE;
|
||
ULONG i;
|
||
WCHAR *DCNameArg;
|
||
UNICODE_STRING DomainNameDns;
|
||
UNICODE_STRING DomainNameFlat;
|
||
UNICODE_STRING DCNameFlat;
|
||
UNICODE_STRING DCName;
|
||
|
||
STD_FSCTRL_PROLOGUE(DfsFsctrlSetDCName, TRUE, FALSE, FALSE);
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsFsctrlSetDCName()\n", 0);
|
||
|
||
RtlZeroMemory(&DomainNameDns, sizeof(UNICODE_STRING));
|
||
RtlZeroMemory(&DomainNameFlat, sizeof(UNICODE_STRING));
|
||
RtlZeroMemory(&DCName, sizeof(UNICODE_STRING));
|
||
RtlZeroMemory(&DCNameFlat, sizeof(UNICODE_STRING));
|
||
|
||
DCNameArg = (WCHAR *)InputBuffer;
|
||
|
||
//
|
||
// We expect a the buffer to be unicode, so it had better be
|
||
// of even length
|
||
//
|
||
|
||
if ((InputBufferLength & 0x1) != 0) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Verify there's a null someplace in the buffer
|
||
//
|
||
|
||
for (i = 0; i < InputBufferLength/sizeof(WCHAR) && DCNameArg[i]; i++)
|
||
NOTHING;
|
||
|
||
if (i >= InputBufferLength/sizeof(WCHAR)) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Verify that the name given (with an added NULL) will fit
|
||
// into a USHORT
|
||
//
|
||
|
||
if ((wcslen(DCNameArg) * sizeof(WCHAR)) > MAXUSHORT - sizeof(WCHAR)) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
GotNewDc = (i > 0) ? TRUE : FALSE;
|
||
|
||
//
|
||
// If we have a new DC name, switch to it
|
||
//
|
||
|
||
if (GotNewDc == TRUE) {
|
||
|
||
UNICODE_STRING NewDCName;
|
||
|
||
DfsDbgTrace(0, Dbg, "DCNameArg=%ws\n", DCNameArg);
|
||
|
||
NewDCName.Length = wcslen(DCNameArg) * sizeof(WCHAR);
|
||
NewDCName.MaximumLength = NewDCName.Length + sizeof(UNICODE_NULL);
|
||
|
||
NewDCName.Buffer = ExAllocatePoolWithTag(PagedPool, NewDCName.MaximumLength, ' puM');
|
||
|
||
if (NewDCName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
PktAcquireExclusive(TRUE, &GotPkt);
|
||
|
||
RtlCopyMemory(NewDCName.Buffer, DCNameArg, NewDCName.MaximumLength);
|
||
|
||
if (Pkt->DCName.Buffer != NULL) {
|
||
ExFreePool(Pkt->DCName.Buffer);
|
||
}
|
||
Pkt->DCName = NewDCName;
|
||
|
||
}
|
||
|
||
//
|
||
// We need to reference the DCName in the Pkt even without the Pkt locked,
|
||
// so we make a copy.
|
||
//
|
||
|
||
if (GotPkt == FALSE) {
|
||
|
||
PktAcquireExclusive(TRUE, &GotPkt);
|
||
|
||
}
|
||
|
||
if (Pkt->DCName.Length > 0) {
|
||
|
||
DFS_DUPLICATE_STRING(DCName,Pkt->DCName.Buffer, Status);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
|
||
if (GotNewDc == TRUE) {
|
||
|
||
if (Pkt->DomainNameDns.Length > 0) {
|
||
|
||
DFS_DUPLICATE_STRING(DomainNameDns,Pkt->DomainNameDns.Buffer, Status);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto CheckSpcTable;
|
||
}
|
||
|
||
}
|
||
|
||
if (Pkt->DomainNameFlat.Length > 0) {
|
||
|
||
DFS_DUPLICATE_STRING(DomainNameFlat,Pkt->DomainNameFlat.Buffer, Status);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto CheckSpcTable;
|
||
}
|
||
|
||
}
|
||
|
||
PktRelease();
|
||
GotPkt = FALSE;
|
||
|
||
if (DCName.Length > 0 && DomainNameDns.Length > 0) {
|
||
|
||
PktpSetActiveSpcService(
|
||
&DomainNameDns,
|
||
&DCName,
|
||
FALSE);
|
||
|
||
DCNameFlat = DCName;
|
||
|
||
for (i = 0;
|
||
i < DCNameFlat.Length / sizeof(WCHAR) && DCNameFlat.Buffer[i] != L'.';
|
||
i++
|
||
) {
|
||
NOTHING;
|
||
}
|
||
|
||
DCNameFlat.Length = (USHORT) (i * sizeof(WCHAR));
|
||
|
||
if (DCNameFlat.Length > Pkt->DCName.Length)
|
||
DCNameFlat.Length = Pkt->DCName.Length;
|
||
|
||
}
|
||
|
||
if (DCNameFlat.Length > 0 && DomainNameFlat.Length > 0) {
|
||
|
||
PktpSetActiveSpcService(
|
||
&DomainNameFlat,
|
||
&DCNameFlat,
|
||
FALSE);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (GotPkt == TRUE) {
|
||
|
||
PktRelease();
|
||
GotPkt = FALSE;
|
||
|
||
}
|
||
|
||
CheckSpcTable:
|
||
|
||
if (NT_SUCCESS(Status) &&
|
||
(Pkt->SpecialTable.SpecialEntryCount == 0 || Pkt->SpecialTable.TimeToLive == 0)) {
|
||
|
||
if (DCName.Length > 0) {
|
||
|
||
Status = PktGetSpecialReferralTable(&DCName, TRUE);
|
||
|
||
} else {
|
||
|
||
Status = STATUS_BAD_NETWORK_PATH;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// Free the local copies
|
||
//
|
||
|
||
if (DomainNameDns.Buffer != NULL)
|
||
ExFreePool(DomainNameDns.Buffer);
|
||
|
||
if (DomainNameFlat.Buffer != NULL)
|
||
ExFreePool(DomainNameFlat.Buffer);
|
||
|
||
if (DCName.Buffer != NULL)
|
||
ExFreePool(DCName.Buffer);
|
||
|
||
if (GotPkt == TRUE) {
|
||
|
||
PktRelease();
|
||
GotPkt = FALSE;
|
||
|
||
}
|
||
|
||
DfsCompleteRequest(IrpContext, Irp, Status);
|
||
DfsDbgTrace(+1, Dbg, "DfsFsctrlSetDCName exit 0x%x\n", ULongToPtr(Status) );
|
||
|
||
return (Status);
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsFsctrlSetDomainNameFlat
|
||
//
|
||
// Synopsis: Sets the DomainName (flat)
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsFsctrlSetDomainNameFlat(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PDFS_PKT Pkt = _GetPkt();
|
||
BOOLEAN GotPkt;
|
||
ULONG i;
|
||
WCHAR *DomainNameFlat;
|
||
|
||
STD_FSCTRL_PROLOGUE(DfsFsctrlSetDomainNameFlat, TRUE, FALSE, FALSE);
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsFsctrlSetDomainNameFlat()\n", 0);
|
||
|
||
DomainNameFlat = (WCHAR *)InputBuffer;
|
||
|
||
//
|
||
// Verify there's a null someplace in the buffer
|
||
//
|
||
|
||
for (i = 0; i < InputBufferLength/sizeof(WCHAR) && DomainNameFlat[i]; i++)
|
||
NOTHING;
|
||
|
||
//
|
||
// Zero-len is as bad as no terminating NULL
|
||
//
|
||
if (i == 0 || i >= InputBufferLength/sizeof(WCHAR)) {
|
||
DfsCompleteRequest(IrpContext, Irp, Status);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Verify that the name given (with an added NULL) will fit
|
||
// into a USHORT
|
||
//
|
||
|
||
if ((wcslen(DomainNameFlat) * sizeof(WCHAR)) > MAXUSHORT - sizeof(WCHAR)) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
DfsCompleteRequest(IrpContext, Irp, Status);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
PktAcquireExclusive(TRUE, &GotPkt);
|
||
|
||
DfsDbgTrace(0, Dbg, "DomainNameFlat=%ws\n", DomainNameFlat);
|
||
|
||
//
|
||
// Replace old
|
||
//
|
||
if (Pkt->DomainNameFlat.Buffer) {
|
||
ExFreePool(Pkt->DomainNameFlat.Buffer);
|
||
}
|
||
|
||
Pkt->DomainNameFlat.Length = wcslen(DomainNameFlat) * sizeof(WCHAR);
|
||
Pkt->DomainNameFlat.MaximumLength = Pkt->DomainNameFlat.Length + sizeof(UNICODE_NULL);
|
||
|
||
Pkt->DomainNameFlat.Buffer = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
Pkt->DomainNameFlat.MaximumLength,
|
||
' puM');
|
||
|
||
if (Pkt->DomainNameFlat.Buffer != NULL) {
|
||
RtlCopyMemory(
|
||
Pkt->DomainNameFlat.Buffer,
|
||
DomainNameFlat,
|
||
Pkt->DomainNameFlat.MaximumLength);
|
||
} else {
|
||
Pkt->DomainNameFlat.Length = Pkt->DomainNameFlat.MaximumLength = 0;
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
PktRelease();
|
||
|
||
DfsCompleteRequest(IrpContext, Irp, Status);
|
||
DfsDbgTrace(+1, Dbg, "DfsFsctrlSetDomainNameFlat exit 0x%x\n", ULongToPtr(Status) );
|
||
|
||
return (Status);
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: DfsFsctrlSetDomainNameDns
|
||
//
|
||
// Synopsis: Sets the DomainName (flat)
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DfsFsctrlSetDomainNameDns(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PDFS_PKT Pkt = _GetPkt();
|
||
BOOLEAN GotPkt;
|
||
ULONG i;
|
||
WCHAR *DomainNameDns;
|
||
|
||
STD_FSCTRL_PROLOGUE(DfsFsctrlSetDomainNameDns, TRUE, FALSE, FALSE);
|
||
|
||
DfsDbgTrace(+1, Dbg, "DfsFsctrlSetDomainNameDns()\n", 0);
|
||
|
||
DomainNameDns = (WCHAR *)InputBuffer;
|
||
|
||
//
|
||
// Verify there's a null someplace in the buffer
|
||
//
|
||
|
||
for (i = 0; i < InputBufferLength/sizeof(WCHAR) && DomainNameDns[i]; i++)
|
||
NOTHING;
|
||
|
||
//
|
||
// Zero-len is as bad as no terminating NULL
|
||
//
|
||
if (i == 0 || i >= InputBufferLength/sizeof(WCHAR)) {
|
||
DfsCompleteRequest(IrpContext, Irp, Status);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Verify that the name given (with an added NULL) will fit
|
||
// into a USHORT
|
||
//
|
||
|
||
if ((wcslen(DomainNameDns) * sizeof(WCHAR)) > MAXUSHORT - sizeof(WCHAR)) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
DfsCompleteRequest(IrpContext, Irp, Status);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
PktAcquireExclusive(TRUE, &GotPkt);
|
||
|
||
DfsDbgTrace(0, Dbg, "DomainNameDns=%ws\n", DomainNameDns);
|
||
|
||
//
|
||
// Replace old
|
||
//
|
||
if (Pkt->DomainNameDns.Buffer) {
|
||
ExFreePool(Pkt->DomainNameDns.Buffer);
|
||
}
|
||
|
||
Pkt->DomainNameDns.Length = wcslen(DomainNameDns) * sizeof(WCHAR);
|
||
Pkt->DomainNameDns.MaximumLength = Pkt->DomainNameDns.Length + sizeof(UNICODE_NULL);
|
||
|
||
Pkt->DomainNameDns.Buffer = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
Pkt->DomainNameDns.MaximumLength,
|
||
' puM');
|
||
|
||
if (Pkt->DomainNameDns.Buffer != NULL) {
|
||
RtlCopyMemory(
|
||
Pkt->DomainNameDns.Buffer,
|
||
DomainNameDns,
|
||
Pkt->DomainNameDns.MaximumLength);
|
||
} else {
|
||
Pkt->DomainNameDns.Length = Pkt->DomainNameDns.MaximumLength = 0;
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
PktRelease();
|
||
|
||
DfsCompleteRequest(IrpContext, Irp, Status);
|
||
DfsDbgTrace(+1, Dbg, "DfsFsctrlSetDomainNameDns exit 0x%x\n", ULongToPtr(Status) );
|
||
|
||
return (Status);
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: PktFsctrlFlushCache, public
|
||
//
|
||
// Synopsis: This function will flush all entries which match the specified
|
||
// input path.
|
||
// However, this function will refuse to delete any Permanent
|
||
// entries of the PKT.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
NTSTATUS
|
||
PktFsctrlFlushCache(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
)
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PDFS_PKT Pkt;
|
||
PDFS_PKT_ENTRY curEntry;
|
||
PDFS_PKT_ENTRY nextEntry;
|
||
PDFS_PKT_ENTRY pEntry;
|
||
BOOLEAN pktLocked;
|
||
UNICODE_STRING ustrPrefix, RemainingPath;
|
||
PWCHAR wCp = (PWCHAR) InputBuffer;
|
||
|
||
STD_FSCTRL_PROLOGUE(PktFsctrlFlushCache, TRUE, FALSE, FALSE);
|
||
|
||
DfsDbgTrace(+1,Dbg, "PktFsctrlFlushCache()\n", 0);
|
||
|
||
//
|
||
// If InputBufferLength == 2 and InputBuffer == '*', flush all entries
|
||
//
|
||
|
||
if (InputBufferLength == sizeof(WCHAR) && wCp[0] == L'*') {
|
||
|
||
Pkt = _GetPkt();
|
||
PktAcquireExclusive(TRUE, &pktLocked);
|
||
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
|
||
curEntry = PktFirstEntry(Pkt);
|
||
|
||
while (curEntry!=NULL) {
|
||
|
||
nextEntry = PktNextEntry(Pkt, curEntry);
|
||
|
||
if ( !(curEntry->Type & PKT_ENTRY_TYPE_PERMANENT) ) {
|
||
|
||
if (curEntry->UseCount == 0) {
|
||
|
||
PktEntryDestroy(curEntry, Pkt, (BOOLEAN) TRUE);
|
||
|
||
} else if ( !(curEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC) ) {
|
||
|
||
//
|
||
// We can't delete this entry because it is in use, so
|
||
// mark it DELETE_PENDING, set its timeout to zero
|
||
// and remove from the prefix tables
|
||
//
|
||
|
||
curEntry->Type |= PKT_ENTRY_TYPE_DELETE_PENDING;
|
||
curEntry->ExpireTime = 0;
|
||
curEntry->USN++;
|
||
DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(curEntry->Id.Prefix));
|
||
DfsRemoveUnicodePrefix(&Pkt->ShortPrefixTable, &(curEntry->Id.ShortPrefix));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
curEntry = nextEntry;
|
||
}
|
||
|
||
PktRelease();
|
||
ExReleaseResourceLite( &DfsData.Resource );
|
||
|
||
DfsCompleteRequest( IrpContext, Irp, status );
|
||
DfsDbgTrace(-1,Dbg, "PktFsctrlFlushCache: Exit -> %08lx\n", ULongToPtr(status) );
|
||
return(status);
|
||
|
||
}
|
||
|
||
//
|
||
// Verify the buffer contains at least a '\' and is of even length
|
||
//
|
||
|
||
if (InputBufferLength < sizeof(WCHAR)
|
||
||
|
||
(InputBufferLength & 0x1) != 0
|
||
||
|
||
wCp[0] != UNICODE_PATH_SEP) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
DfsCompleteRequest( IrpContext, Irp, status );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// Flush one entry
|
||
//
|
||
|
||
ustrPrefix.Length = (USHORT) InputBufferLength;
|
||
ustrPrefix.MaximumLength = (USHORT) InputBufferLength;
|
||
ustrPrefix.Buffer = (PWCHAR) InputBuffer;
|
||
|
||
if (ustrPrefix.Length >= sizeof(WCHAR) * 2 &&
|
||
ustrPrefix.Buffer[0] == UNICODE_PATH_SEP &&
|
||
ustrPrefix.Buffer[1] == UNICODE_PATH_SEP
|
||
) {
|
||
ustrPrefix.Buffer++;
|
||
ustrPrefix.Length -= sizeof(WCHAR);
|
||
}
|
||
|
||
if (ustrPrefix.Buffer[ustrPrefix.Length/sizeof(WCHAR)-1] == UNICODE_NULL) {
|
||
ustrPrefix.Length -= sizeof(WCHAR);
|
||
}
|
||
|
||
Pkt = _GetPkt();
|
||
|
||
PktAcquireExclusive(TRUE, &pktLocked);
|
||
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
|
||
|
||
pEntry = PktLookupEntryByPrefix(Pkt,
|
||
&ustrPrefix,
|
||
&RemainingPath);
|
||
|
||
if (pEntry == NULL || RemainingPath.Length != 0) {
|
||
|
||
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
|
||
} else {
|
||
|
||
if ( !(pEntry->Type & PKT_ENTRY_TYPE_PERMANENT) ) {
|
||
|
||
if (pEntry->UseCount == 0) {
|
||
|
||
PktEntryDestroy(pEntry, Pkt, (BOOLEAN) TRUE);
|
||
|
||
} else if ( !(pEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC) ) {
|
||
|
||
//
|
||
// We can't delete this entry because it is in use, so
|
||
// mark it DELETE_PENDING, set its timeout to zero
|
||
// and remove from the prefix tables
|
||
//
|
||
|
||
pEntry->Type |= PKT_ENTRY_TYPE_DELETE_PENDING;
|
||
pEntry->ExpireTime = 0;
|
||
DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(pEntry->Id.Prefix));
|
||
DfsRemoveUnicodePrefix(&Pkt->ShortPrefixTable, &(pEntry->Id.ShortPrefix));
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
PktRelease();
|
||
ExReleaseResourceLite( &DfsData.Resource );
|
||
|
||
DfsCompleteRequest( IrpContext, Irp, status );
|
||
DfsDbgTrace(-1,Dbg, "PktFsctrlFlushCache: Exit -> %08lx\n", ULongToPtr(status) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: PktFsctrlFlushSpcCache, public
|
||
//
|
||
// Synopsis: This function will flush all entries which match the specified
|
||
// input path.
|
||
// However, this function will refuse to delete any Permanent
|
||
// entries of the PKT.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
NTSTATUS
|
||
PktFsctrlFlushSpcCache(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
)
|
||
{
|
||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||
PDFS_PKT Pkt;
|
||
BOOLEAN pktLocked;
|
||
PDFS_SPECIAL_ENTRY pSpecialEntry;
|
||
PDFS_SPECIAL_TABLE pSpecialTable;
|
||
PWCHAR wCp = (PWCHAR) InputBuffer;
|
||
ULONG i;
|
||
|
||
STD_FSCTRL_PROLOGUE(PktFsctrlFlushSpcCache, TRUE, FALSE, FALSE);
|
||
|
||
DfsDbgTrace(+1,Dbg, "PktFsctrlFlushSpcCache()\n", 0);
|
||
|
||
//
|
||
// InputBufferLength == 2 and InputBuffer == '*'
|
||
//
|
||
|
||
if (InputBufferLength == sizeof(WCHAR) && wCp[0] == L'*') {
|
||
|
||
Pkt = _GetPkt();
|
||
PktAcquireExclusive(TRUE, &pktLocked);
|
||
pSpecialTable = &Pkt->SpecialTable;
|
||
|
||
pSpecialTable->TimeToLive = 0;
|
||
|
||
pSpecialEntry = CONTAINING_RECORD(
|
||
pSpecialTable->SpecialEntryList.Flink,
|
||
DFS_SPECIAL_ENTRY,
|
||
Link);
|
||
|
||
for (i = 0; i < pSpecialTable->SpecialEntryCount; i++) {
|
||
|
||
pSpecialEntry->Stale = TRUE;
|
||
|
||
pSpecialEntry = CONTAINING_RECORD(
|
||
pSpecialEntry->Link.Flink,
|
||
DFS_SPECIAL_ENTRY,
|
||
Link);
|
||
}
|
||
|
||
PktRelease();
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
DfsCompleteRequest( IrpContext, Irp, status );
|
||
DfsDbgTrace(-1,Dbg, "PktFsctrlFlushSpcCache: Exit -> %08lx\n", ULongToPtr(status) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: PktFlushChildren
|
||
//
|
||
// Synopsis: This function will flush all entries which are children
|
||
// of the entry passed in.
|
||
// However, this function will refuse to delete any Permanent
|
||
// entries of the PKT.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
VOID
|
||
PktFlushChildren(
|
||
PDFS_PKT_ENTRY pEntry
|
||
)
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PDFS_PKT Pkt;
|
||
PDFS_PKT_ENTRY curEntry;
|
||
PDFS_PKT_ENTRY nextEntry;
|
||
BOOLEAN pktLocked;
|
||
|
||
DfsDbgTrace(+1,Dbg, "PktFlushChildren(%wZ)\n", &pEntry->Id.Prefix);
|
||
|
||
#if DBG
|
||
if (MupVerbose)
|
||
DbgPrint("PktFlushChildren(%wZ)\n", &pEntry->Id.Prefix);
|
||
#endif
|
||
|
||
PktAcquireExclusive(TRUE, &pktLocked);
|
||
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
|
||
|
||
Pkt = _GetPkt();
|
||
|
||
curEntry = PktEntryFirstChild(pEntry);
|
||
|
||
while (curEntry != NULL) {
|
||
|
||
DfsDbgTrace(0, Dbg, "PktFlushChildren: examining %wZ\n",
|
||
&curEntry->Id.Prefix);
|
||
//
|
||
// We may lose this entry due to deletion. Let us get the Next
|
||
// entry before we go into the next stage.
|
||
//
|
||
|
||
nextEntry = PktEntryNextChild(pEntry,curEntry);
|
||
|
||
//
|
||
// Try to delete the entry.
|
||
//
|
||
|
||
if ( !(curEntry->Type & PKT_ENTRY_TYPE_PERMANENT) ) {
|
||
|
||
if (curEntry->UseCount == 0) {
|
||
|
||
PktEntryDestroy(curEntry, Pkt, (BOOLEAN) TRUE);
|
||
|
||
} else if ( !(curEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC) ) {
|
||
|
||
//
|
||
// We can't delete this entry because it is in use, so
|
||
// mark it DELETE_PENDING, set its timeout to zero
|
||
// and remove from the prefix tables
|
||
//
|
||
|
||
curEntry->Type |= PKT_ENTRY_TYPE_DELETE_PENDING;
|
||
curEntry->ExpireTime = 0;
|
||
DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(curEntry->Id.Prefix));
|
||
DfsRemoveUnicodePrefix(&Pkt->ShortPrefixTable, &(curEntry->Id.ShortPrefix));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
curEntry = nextEntry;
|
||
|
||
}
|
||
|
||
PktRelease();
|
||
ExReleaseResourceLite( &DfsData.Resource );
|
||
|
||
#if DBG
|
||
if (MupVerbose)
|
||
DbgPrint("PktFlushChildren returning VOID\n");
|
||
#endif
|
||
|
||
DfsDbgTrace(-1,Dbg, "PktFlushChildren returning VOID\n", 0);
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: PktpSetActiveSpcService
|
||
//
|
||
// Synopsis: This function will attempt to set the 'active' DC in the specified
|
||
// domain
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns: STATUS_SUCCESS or STATUS_NOT_FOUND
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
NTSTATUS
|
||
PktpSetActiveSpcService(
|
||
PUNICODE_STRING DomainName,
|
||
PUNICODE_STRING DcName,
|
||
BOOLEAN ResetTimeout)
|
||
{
|
||
NTSTATUS status = STATUS_NOT_FOUND;
|
||
ULONG EntryIdx;
|
||
USHORT i;
|
||
PDFS_SPECIAL_ENTRY pSpecialEntry;
|
||
UNICODE_STRING DcNameFlat;
|
||
BOOLEAN pktLocked;
|
||
|
||
if (DomainName != NULL && DomainName->Length > 0) {
|
||
|
||
status = PktExpandSpecialName(DomainName, &pSpecialEntry);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
for (EntryIdx = 0; EntryIdx < pSpecialEntry->ExpandedCount; EntryIdx++) {
|
||
|
||
if (RtlCompareUnicodeString(
|
||
DcName,
|
||
&pSpecialEntry->ExpandedNames[EntryIdx].ExpandedName,
|
||
TRUE) == 0) {
|
||
|
||
pSpecialEntry->Active = EntryIdx;
|
||
//
|
||
// Keep the spc table around for a while longer
|
||
//
|
||
if (ResetTimeout == TRUE) {
|
||
PktAcquireExclusive(TRUE, &pktLocked);
|
||
if (DfsData.Pkt.SpecialTable.TimeToLive < 60 * 15) {
|
||
DfsData.Pkt.SpecialTable.TimeToLive += 60 * 15; // 15 min
|
||
}
|
||
PktRelease();
|
||
}
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
}
|
||
|
||
status = STATUS_NOT_FOUND;
|
||
|
||
}
|
||
|
||
InterlockedDecrement(&pSpecialEntry->UseCount);
|
||
|
||
} else {
|
||
|
||
status = STATUS_NOT_FOUND;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|