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.
1251 lines
34 KiB
1251 lines
34 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
NTSTATUS DiscardStatus;
|
|
|
|
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++;
|
|
DiscardStatus = DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(curEntry->Id.Prefix));
|
|
DiscardStatus = 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;
|
|
DiscardStatus = DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(pEntry->Id.Prefix));
|
|
DiscardStatus = 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;
|
|
NTSTATUS DiscardStatus;
|
|
|
|
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;
|
|
DiscardStatus = DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(curEntry->Id.Prefix));
|
|
DiscardStatus = 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;
|
|
|
|
}
|