//+---------------------------------------------------------------------------- // // 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 // // 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; }