1229 lines
27 KiB
C
1229 lines
27 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
bowname.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements all of the routines to manage the NT bowser name
|
|||
|
manipulation routines
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Larry Osterman (LarryO) 21-Jun-1990
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
21-Jun-1990 LarryO
|
|||
|
|
|||
|
Created
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
typedef struct _ENUM_NAMES_CONTEXT {
|
|||
|
PDGRECEIVE_NAMES OutputBuffer;
|
|||
|
PDGRECEIVE_NAMES NextOutputBuffer;
|
|||
|
PVOID OutputBufferEnd;
|
|||
|
ULONG OutputBufferSize;
|
|||
|
ULONG EntriesRead;
|
|||
|
ULONG TotalEntries;
|
|||
|
ULONG TotalBytesNeeded;
|
|||
|
ULONG_PTR OutputBufferDisplacement;
|
|||
|
} ENUM_NAMES_CONTEXT, *PENUM_NAMES_CONTEXT;
|
|||
|
|
|||
|
typedef struct _ADD_TRANSPORT_NAME_CONTEXT {
|
|||
|
LIST_ENTRY ListHead;
|
|||
|
UNICODE_STRING NameToAdd;
|
|||
|
DGRECEIVER_NAME_TYPE NameType;
|
|||
|
} ADD_TRANSPORT_NAME_CONTEXT, *PADD_TRANSPORT_NAME_CONTEXT;
|
|||
|
|
|||
|
typedef struct _ADD_TRANSPORT_NAME_STRUCTURE {
|
|||
|
LIST_ENTRY Link;
|
|||
|
HANDLE ThreadHandle;
|
|||
|
PTRANSPORT Transport;
|
|||
|
UNICODE_STRING NameToAdd;
|
|||
|
DGRECEIVER_NAME_TYPE NameType;
|
|||
|
NTSTATUS Status;
|
|||
|
} ADD_TRANSPORT_NAME_STRUCTURE, *PADD_TRANSPORT_NAME_STRUCTURE;
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
AddTransportName(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
AsyncCreateTransportName(
|
|||
|
IN PVOID Ctx
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
WaitForAddNameOperation(
|
|||
|
IN PADD_TRANSPORT_NAME_CONTEXT Context
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserDeleteNamesInDomain(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN PUNICODE_STRING Name OPTIONAL,
|
|||
|
IN DGRECEIVER_NAME_TYPE NameType
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserDeleteNamesWorker(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN OUT PVOID Ctx
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
EnumerateNamesTransportWorker(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN OUT PVOID Ctx
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
EnumerateNamesTransportNameWorker(
|
|||
|
IN PTRANSPORT_NAME TransportName,
|
|||
|
IN OUT PVOID Ctx
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, BowserAllocateName)
|
|||
|
#pragma alloc_text(PAGE, BowserAddDefaultNames)
|
|||
|
#pragma alloc_text(PAGE, BowserDeleteDefaultDomainNames)
|
|||
|
#pragma alloc_text(PAGE, AddTransportName)
|
|||
|
#pragma alloc_text(PAGE, AsyncCreateTransportName)
|
|||
|
#pragma alloc_text(PAGE, WaitForAddNameOperation)
|
|||
|
#pragma alloc_text(PAGE, BowserDeleteNameByName)
|
|||
|
#pragma alloc_text(PAGE, BowserDereferenceName)
|
|||
|
#pragma alloc_text(PAGE, BowserReferenceName)
|
|||
|
#pragma alloc_text(PAGE, BowserForEachName)
|
|||
|
#pragma alloc_text(PAGE, BowserDeleteName)
|
|||
|
#pragma alloc_text(PAGE, BowserDeleteNamesInDomain)
|
|||
|
#pragma alloc_text(PAGE, BowserDeleteNamesWorker)
|
|||
|
#pragma alloc_text(PAGE, BowserFindName)
|
|||
|
#pragma alloc_text(PAGE, BowserEnumerateNamesInDomain)
|
|||
|
#pragma alloc_text(PAGE, EnumerateNamesTransportWorker)
|
|||
|
#pragma alloc_text(PAGE, EnumerateNamesTransportNameWorker)
|
|||
|
#pragma alloc_text(INIT, BowserpInitializeNames)
|
|||
|
#pragma alloc_text(PAGE, BowserpUninitializeNames)
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserAllocateName(
|
|||
|
IN PUNICODE_STRING NameToAdd,
|
|||
|
IN DGRECEIVER_NAME_TYPE NameType,
|
|||
|
IN PTRANSPORT Transport OPTIONAL,
|
|||
|
IN PDOMAIN_INFO DomainInfo OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine creates a browser name
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NameToAdd - Netbios name to add to one or more transports
|
|||
|
|
|||
|
NameType - Type of the added name
|
|||
|
|
|||
|
Transport - if specified, the name is added to this transport.
|
|||
|
If not specified, the name is added to all transports in the domain.
|
|||
|
|
|||
|
DomainInfo - Specifies the emulated domain to add the name to.
|
|||
|
If not specified, the name is added to the specified transport.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of resulting operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PBOWSER_NAME NewName;
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
OEM_STRING OemName;
|
|||
|
BOOLEAN ResourceLocked = FALSE;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ExAcquireResourceExclusiveLite(&BowserTransportDatabaseResource, TRUE);
|
|||
|
|
|||
|
ResourceLocked = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// If the name doesn't already exist,
|
|||
|
// allocate one and fill it in.
|
|||
|
//
|
|||
|
|
|||
|
NewName = BowserFindName(NameToAdd, NameType);
|
|||
|
|
|||
|
if (NewName == NULL) {
|
|||
|
|
|||
|
NewName = ALLOCATE_POOL( PagedPool,
|
|||
|
sizeof(BOWSER_NAME) +
|
|||
|
NameToAdd->Length+sizeof(WCHAR),
|
|||
|
POOL_BOWSERNAME);
|
|||
|
|
|||
|
if (NewName == NULL) {
|
|||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
NewName->Signature = STRUCTURE_SIGNATURE_BOWSER_NAME;
|
|||
|
|
|||
|
NewName->Size = sizeof(BOWSER_NAME);
|
|||
|
|
|||
|
// This reference matches the one FindName would have done
|
|||
|
// above had it succeeded.
|
|||
|
NewName->ReferenceCount = 1;
|
|||
|
|
|||
|
InitializeListHead(&NewName->NameChain);
|
|||
|
|
|||
|
NewName->NameType = NameType;
|
|||
|
|
|||
|
InsertHeadList(&BowserNameHead, &NewName->GlobalNext);
|
|||
|
|
|||
|
NewName->Name.Buffer = (LPWSTR)(NewName+1);
|
|||
|
NewName->Name.MaximumLength = NameToAdd->Length + sizeof(WCHAR);
|
|||
|
RtlCopyUnicodeString(&NewName->Name, NameToAdd);
|
|||
|
|
|||
|
//
|
|||
|
// Null terminate the name in the buffer just in case.
|
|||
|
//
|
|||
|
|
|||
|
NewName->Name.Buffer[NewName->Name.Length/sizeof(WCHAR)] = L'\0';
|
|||
|
|
|||
|
//
|
|||
|
// Uppercase the name.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlUpcaseUnicodeStringToOemString(&OemName, &NewName->Name, TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlOemStringToUnicodeString(&NewName->Name, &OemName, FALSE);
|
|||
|
|
|||
|
RtlFreeOemString(&OemName);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(Transport)) {
|
|||
|
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
ResourceLocked = FALSE;
|
|||
|
|
|||
|
Status = BowserCreateTransportName(Transport, NewName);
|
|||
|
} else {
|
|||
|
ADD_TRANSPORT_NAME_CONTEXT context;
|
|||
|
|
|||
|
context.NameToAdd = *NameToAdd;
|
|||
|
context.NameType = NameType;
|
|||
|
|
|||
|
InitializeListHead(&context.ListHead);
|
|||
|
|
|||
|
Status = BowserForEachTransportInDomain( DomainInfo, AddTransportName, &context);
|
|||
|
|
|||
|
//
|
|||
|
// Since we will reference this name and transport while we
|
|||
|
// are processing the list, we want to release the database resource
|
|||
|
// now.
|
|||
|
//
|
|||
|
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
ResourceLocked = FALSE;
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
WaitForAddNameOperation(&context);
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
Status = WaitForAddNameOperation(&context);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ReturnStatus:
|
|||
|
|
|||
|
if (ResourceLocked) {
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Delete this transport.
|
|||
|
//
|
|||
|
|
|||
|
if (NewName != NULL) {
|
|||
|
|
|||
|
if (!ARGUMENT_PRESENT(Transport)) {
|
|||
|
|
|||
|
//
|
|||
|
// Clean out any names that we may have added already.
|
|||
|
//
|
|||
|
|
|||
|
BowserDeleteNamesInDomain( DomainInfo, &NewName->Name, NewName->NameType );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (NewName != NULL) {
|
|||
|
BowserDereferenceName(NewName);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserAddDefaultNames(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Add the default names for a newly created transport.
|
|||
|
|
|||
|
Add the ComputerName<00>, Domain<00>, Domain<1C>, and other domains.
|
|||
|
|
|||
|
All of the newly added names are added in parallel to increase performance.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Transport - The names are added to this transport.
|
|||
|
|
|||
|
Context - If specified, a pointer to the UNICODE_STRING structure specifying
|
|||
|
the domain name to register.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of resulting operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
NTSTATUS TempStatus;
|
|||
|
|
|||
|
PLIST_ENTRY NameEntry;
|
|||
|
|
|||
|
ADD_TRANSPORT_NAME_CONTEXT AddNameContext;
|
|||
|
PDOMAIN_INFO DomainInfo = Transport->DomainInfo;
|
|||
|
|
|||
|
UNICODE_STRING EmulatedComputerName;
|
|||
|
UNICODE_STRING EmulatedDomainName;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Build the domain name and computer name to add.
|
|||
|
//
|
|||
|
|
|||
|
EmulatedComputerName = DomainInfo->DomUnicodeComputerName;
|
|||
|
|
|||
|
if ( Context == NULL ) {
|
|||
|
EmulatedDomainName = DomainInfo->DomUnicodeDomainName;
|
|||
|
} else {
|
|||
|
EmulatedDomainName = *((PUNICODE_STRING)Context);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the queue of threads
|
|||
|
//
|
|||
|
|
|||
|
InitializeListHead(&AddNameContext.ListHead);
|
|||
|
|
|||
|
//
|
|||
|
// Add the computer<00> name
|
|||
|
//
|
|||
|
|
|||
|
AddNameContext.NameToAdd = EmulatedComputerName;
|
|||
|
AddNameContext.NameType = ComputerName;
|
|||
|
|
|||
|
Status = AddTransportName( Transport, &AddNameContext);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add the domain<00> name
|
|||
|
//
|
|||
|
|
|||
|
AddNameContext.NameToAdd = EmulatedDomainName;
|
|||
|
AddNameContext.NameType = PrimaryDomain;
|
|||
|
|
|||
|
Status = AddTransportName( Transport, &AddNameContext);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add the domain<1C> name
|
|||
|
//
|
|||
|
|
|||
|
if (BowserData.IsLanmanNt) {
|
|||
|
AddNameContext.NameToAdd = EmulatedDomainName;
|
|||
|
AddNameContext.NameType = DomainName;
|
|||
|
|
|||
|
Status = AddTransportName( Transport, &AddNameContext);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add each of the OtherDomain<00> names
|
|||
|
//
|
|||
|
|
|||
|
ExAcquireResourceExclusiveLite(&BowserTransportDatabaseResource, TRUE);
|
|||
|
for (NameEntry = BowserNameHead.Flink;
|
|||
|
NameEntry != &BowserNameHead ;
|
|||
|
NameEntry = NameEntry->Flink) {
|
|||
|
|
|||
|
PBOWSER_NAME Name = CONTAINING_RECORD(NameEntry, BOWSER_NAME, GlobalNext);
|
|||
|
|
|||
|
//
|
|||
|
// Only add the OtherDomains
|
|||
|
//
|
|||
|
|
|||
|
if ( Name->NameType == OtherDomain ) {
|
|||
|
AddNameContext.NameToAdd = Name->Name;
|
|||
|
AddNameContext.NameType = OtherDomain;
|
|||
|
|
|||
|
Status = AddTransportName( Transport, &AddNameContext);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
goto ReturnStatus;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
ReturnStatus:
|
|||
|
|
|||
|
//
|
|||
|
// Wait for any started threads to complete.
|
|||
|
//
|
|||
|
|
|||
|
TempStatus = WaitForAddNameOperation(&AddNameContext);
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
Status = TempStatus;
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserDeleteDefaultDomainNames(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Worker routine to re-add all of the default names for the transport.
|
|||
|
|
|||
|
This routine will be called when the domain is renamed. All of the previous
|
|||
|
default names should be removed and the new default names should be added.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Transport - Transport to add the names on.
|
|||
|
|
|||
|
Context - Pointer to UNICODE_STRING identifying the domain name to remove
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of resulting operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PUNICODE_STRING NameToRemove = (PUNICODE_STRING) Context;
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// This is a cleanup operation. Don't fail if we can't remove the name.
|
|||
|
//
|
|||
|
(VOID) BowserDeleteTransportNameByName( Transport, NameToRemove, PrimaryDomain );
|
|||
|
(VOID) BowserDeleteTransportNameByName( Transport, NameToRemove, DomainName );
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
WaitForAddNameOperation(
|
|||
|
IN PADD_TRANSPORT_NAME_CONTEXT Context
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
NTSTATUS LocalStatus;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
while (!IsListEmpty(&Context->ListHead)) {
|
|||
|
PLIST_ENTRY Entry;
|
|||
|
PADD_TRANSPORT_NAME_STRUCTURE addNameStruct;
|
|||
|
|
|||
|
Entry = RemoveHeadList(&Context->ListHead);
|
|||
|
addNameStruct = CONTAINING_RECORD(Entry, ADD_TRANSPORT_NAME_STRUCTURE, Link);
|
|||
|
|
|||
|
//
|
|||
|
// We need to call the Nt version of this API, since we only have
|
|||
|
// the handle to the thread.
|
|||
|
//
|
|||
|
// Also note that we call the Nt version of the API. This works
|
|||
|
// because we are running in the FSP, and thus PreviousMode is Kernel.
|
|||
|
//
|
|||
|
|
|||
|
LocalStatus = ZwWaitForSingleObject(addNameStruct->ThreadHandle,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
|
|||
|
ASSERT (NT_SUCCESS(LocalStatus));
|
|||
|
|
|||
|
LocalStatus = ZwClose(addNameStruct->ThreadHandle);
|
|||
|
|
|||
|
ASSERT (NT_SUCCESS(LocalStatus));
|
|||
|
|
|||
|
//
|
|||
|
// We've waited for this name to be added, now check its status.
|
|||
|
//
|
|||
|
|
|||
|
if (!NT_SUCCESS(addNameStruct->Status)) {
|
|||
|
status = addNameStruct->Status;
|
|||
|
}
|
|||
|
|
|||
|
FREE_POOL(addNameStruct);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we were able to successfully add all the names, then Status will
|
|||
|
// still be STATUS_SUCCESS, however if any of the addnames failed,
|
|||
|
// Status will be set to the status of whichever one of them failed.
|
|||
|
//
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
}
|
|||
|
NTSTATUS
|
|||
|
AddTransportName(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN PVOID Ctx
|
|||
|
)
|
|||
|
{
|
|||
|
PADD_TRANSPORT_NAME_CONTEXT context = Ctx;
|
|||
|
PADD_TRANSPORT_NAME_STRUCTURE addNameStructure;
|
|||
|
NTSTATUS status;
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
addNameStructure = ALLOCATE_POOL(PagedPool, sizeof(ADD_TRANSPORT_NAME_STRUCTURE), POOL_ADDNAME_STRUCT);
|
|||
|
|
|||
|
if (addNameStructure == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
addNameStructure->ThreadHandle = NULL;
|
|||
|
|
|||
|
addNameStructure->Transport = Transport;
|
|||
|
|
|||
|
if ( Transport )
|
|||
|
{
|
|||
|
// reference transport so it doesn't get deleted under us.
|
|||
|
BowserReferenceTransport(Transport);
|
|||
|
}
|
|||
|
|
|||
|
addNameStructure->NameToAdd = context->NameToAdd;
|
|||
|
addNameStructure->NameType = context->NameType;
|
|||
|
|
|||
|
status = PsCreateSystemThread(&addNameStructure->ThreadHandle,
|
|||
|
THREAD_ALL_ACCESS,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
AsyncCreateTransportName,
|
|||
|
addNameStructure);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
if ( Transport )
|
|||
|
{
|
|||
|
// dereference transport upon failure
|
|||
|
BowserDereferenceTransport(Transport);
|
|||
|
}
|
|||
|
|
|||
|
FREE_POOL(addNameStructure);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
InsertTailList(&context->ListHead, &addNameStructure->Link);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
AsyncCreateTransportName(
|
|||
|
IN PVOID Ctx
|
|||
|
)
|
|||
|
{
|
|||
|
PADD_TRANSPORT_NAME_STRUCTURE context = Ctx;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
context->Status = BowserAllocateName(
|
|||
|
&context->NameToAdd,
|
|||
|
context->NameType,
|
|||
|
context->Transport,
|
|||
|
NULL );
|
|||
|
|
|||
|
if ( context->Transport )
|
|||
|
{
|
|||
|
// referenced in calling AddTransportName()
|
|||
|
BowserDereferenceTransport(context->Transport);
|
|||
|
}
|
|||
|
//
|
|||
|
// We're all done with this thread, terminate now.
|
|||
|
//
|
|||
|
|
|||
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserDeleteNameByName(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN PUNICODE_STRING NameToDelete,
|
|||
|
IN DGRECEIVER_NAME_TYPE NameType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine deletes a browser name
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PBOWSER_NAME Name - Supplies a transport structure describing the
|
|||
|
transport address object to be created.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of resulting operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PBOWSER_NAME Name = NULL;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
// DbgBreakPoint();
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the caller is deleting a specific name,
|
|||
|
// ensure it exists.
|
|||
|
//
|
|||
|
|
|||
|
if ( NameToDelete != NULL && NameToDelete->Length != 0 ) {
|
|||
|
Name = BowserFindName(NameToDelete, NameType);
|
|||
|
|
|||
|
if (Name == NULL) {
|
|||
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there are still any names associated with this name,
|
|||
|
// delete them.
|
|||
|
//
|
|||
|
|
|||
|
Status = BowserDeleteNamesInDomain( DomainInfo, NameToDelete, NameType );
|
|||
|
|
|||
|
//
|
|||
|
// Remove the reference from the FindName.
|
|||
|
//
|
|||
|
|
|||
|
if ( Name != NULL ) {
|
|||
|
BowserDereferenceName(Name);
|
|||
|
}
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
BowserDereferenceName (
|
|||
|
IN PBOWSER_NAME Name
|
|||
|
)
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
ExAcquireResourceExclusiveLite(&BowserTransportDatabaseResource, TRUE);
|
|||
|
|
|||
|
Name->ReferenceCount -= 1;
|
|||
|
|
|||
|
if (Name->ReferenceCount == 0) {
|
|||
|
BowserDeleteName(Name);
|
|||
|
}
|
|||
|
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
BowserReferenceName (
|
|||
|
IN PBOWSER_NAME Name
|
|||
|
)
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
ExAcquireResourceExclusiveLite(&BowserTransportDatabaseResource, TRUE);
|
|||
|
|
|||
|
Name->ReferenceCount += 1;
|
|||
|
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserForEachName (
|
|||
|
IN PNAME_ENUM_ROUTINE Routine,
|
|||
|
IN OUT PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will enumerate the names and call back the enum
|
|||
|
routine provided with each names.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Final status of request.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY NameEntry, NextEntry;
|
|||
|
PBOWSER_NAME Name = NULL;
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ExAcquireResourceExclusiveLite(&BowserTransportDatabaseResource, TRUE);
|
|||
|
|
|||
|
for (NameEntry = BowserNameHead.Flink ;
|
|||
|
NameEntry != &BowserNameHead ;
|
|||
|
NameEntry = NextEntry) {
|
|||
|
|
|||
|
Name = CONTAINING_RECORD(NameEntry, BOWSER_NAME, GlobalNext);
|
|||
|
|
|||
|
BowserReferenceName(Name);
|
|||
|
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
|
|||
|
Status = (Routine)(Name, Context);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
BowserDereferenceName(Name);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
ExAcquireResourceExclusiveLite(&BowserTransportDatabaseResource, TRUE);
|
|||
|
|
|||
|
NextEntry = Name->GlobalNext.Flink;
|
|||
|
|
|||
|
BowserDereferenceName(Name);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserDeleteName(
|
|||
|
IN PBOWSER_NAME Name
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine deletes a browser name
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PBOWSER_NAME Name - Supplies a transport structure describing the
|
|||
|
transport address object to be created.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of resulting operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
RemoveEntryList(&Name->GlobalNext);
|
|||
|
|
|||
|
FREE_POOL(Name);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserDeleteNamesInDomain(
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN PUNICODE_STRING Name OPTIONAL,
|
|||
|
IN DGRECEIVER_NAME_TYPE NameType
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine deletes all the transport names associated with a browser name
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Identifies the emulated domain to have the specified names removed.
|
|||
|
|
|||
|
Name - Specifies the transport name to delete.
|
|||
|
If not specified, all names of the specified name type are deleted.
|
|||
|
|
|||
|
NameType - Specifies the name type of the name.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Status of resulting operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
BOWSER_NAME BowserName;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
BowserName.Name = *Name;
|
|||
|
BowserName.NameType = NameType;
|
|||
|
|
|||
|
Status = BowserForEachTransportInDomain( DomainInfo, BowserDeleteNamesWorker, &BowserName );
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserDeleteNamesWorker(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN OUT PVOID Ctx
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the worker routine for BowserDeleteNamesInDomain.
|
|||
|
|
|||
|
Delete all the specified name for the specified transport.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PBOWSER_NAME Name = (PBOWSER_NAME) Ctx;
|
|||
|
// Note the caller doesn't pass a real PBOWSER_NAME.
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Delete all the specified names for the specified transport.
|
|||
|
//
|
|||
|
|
|||
|
Status = BowserDeleteTransportNameByName( Transport, &Name->Name, Name->NameType );
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
PBOWSER_NAME
|
|||
|
BowserFindName (
|
|||
|
IN PUNICODE_STRING NameToFind,
|
|||
|
IN DGRECEIVER_NAME_TYPE NameType
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine scans the bowser name database to find a particular bowser name
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NameToFind - Supplies the name to find.
|
|||
|
|
|||
|
NameType - Type of name to find
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PBOWSER_NAME - Returns the name found.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY NameEntry;
|
|||
|
PBOWSER_NAME Name;
|
|||
|
NTSTATUS Status;
|
|||
|
OEM_STRING OemName;
|
|||
|
UNICODE_STRING UpcasedName;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Uppercase the name.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlUpcaseUnicodeStringToOemString(&OemName, NameToFind, TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlOemStringToUnicodeString(&UpcasedName, &OemName, TRUE);
|
|||
|
|
|||
|
RtlFreeOemString(&OemName);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Loop through the list of names finding this one.
|
|||
|
//
|
|||
|
|
|||
|
ExAcquireResourceExclusiveLite(&BowserTransportDatabaseResource, TRUE);
|
|||
|
|
|||
|
Name = NULL;
|
|||
|
for (NameEntry = BowserNameHead.Flink ;
|
|||
|
NameEntry != &BowserNameHead ;
|
|||
|
NameEntry = NameEntry->Flink) {
|
|||
|
|
|||
|
Name = CONTAINING_RECORD(NameEntry, BOWSER_NAME, GlobalNext);
|
|||
|
|
|||
|
if ( Name->NameType == NameType &&
|
|||
|
RtlEqualUnicodeString( &Name->Name, &UpcasedName, FALSE ) ) {
|
|||
|
|
|||
|
Name->ReferenceCount += 1;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Name = NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
RtlFreeUnicodeString( &UpcasedName );
|
|||
|
ExReleaseResourceLite(&BowserTransportDatabaseResource);
|
|||
|
return Name;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserEnumerateNamesInDomain (
|
|||
|
IN PDOMAIN_INFO DomainInfo,
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
OUT PVOID OutputBuffer,
|
|||
|
OUT ULONG OutputBufferLength,
|
|||
|
IN OUT PULONG EntriesRead,
|
|||
|
IN OUT PULONG TotalEntries,
|
|||
|
IN OUT PULONG TotalBytesNeeded,
|
|||
|
IN ULONG_PTR OutputBufferDisplacement
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will enumerate all the names currently registered by any
|
|||
|
transport.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DomainInfo - Emulated domain the names are to be enumerated for.
|
|||
|
Transport - Transport names are registered on
|
|||
|
NULL - Any transport.
|
|||
|
OutputBuffer - Buffer to fill with name info.
|
|||
|
OutputBufferSize - Filled in with size of buffer.
|
|||
|
EntriesRead - Filled in with the # of entries returned.
|
|||
|
TotalEntries - Filled in with the total # of entries.
|
|||
|
TotalBytesNeeded - Filled in with the # of bytes needed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PVOID OutputBufferEnd;
|
|||
|
NTSTATUS Status;
|
|||
|
ENUM_NAMES_CONTEXT Context;
|
|||
|
PVOID TempOutputBuffer;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
TempOutputBuffer = ALLOCATE_POOL(PagedPool,OutputBufferLength,POOL_NAME_ENUM_BUFFER);
|
|||
|
if (TempOutputBuffer == NULL) {
|
|||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
|
|||
|
OutputBufferEnd = (PCHAR)TempOutputBuffer+OutputBufferLength;
|
|||
|
|
|||
|
Context.EntriesRead = 0;
|
|||
|
Context.TotalEntries = 0;
|
|||
|
Context.TotalBytesNeeded = 0;
|
|||
|
|
|||
|
try {
|
|||
|
Context.OutputBufferSize = OutputBufferLength;
|
|||
|
Context.NextOutputBuffer = Context.OutputBuffer = (PDGRECEIVE_NAMES) TempOutputBuffer;
|
|||
|
Context.OutputBufferDisplacement = (ULONG_PTR)((PCHAR)TempOutputBuffer - ((PCHAR)OutputBuffer - OutputBufferDisplacement));
|
|||
|
Context.OutputBufferEnd = OutputBufferEnd;
|
|||
|
|
|||
|
// DbgPrint("Enumerate Names: Buffer: %lx, BufferSize: %lx, BufferEnd: %lx\n",
|
|||
|
// TempOutputBuffer, OutputBufferLength, OutputBufferEnd);
|
|||
|
|
|||
|
if ( Transport == NULL ) {
|
|||
|
Status = BowserForEachTransportInDomain(DomainInfo, EnumerateNamesTransportWorker, &Context);
|
|||
|
} else {
|
|||
|
Status = EnumerateNamesTransportWorker( Transport, &Context);
|
|||
|
}
|
|||
|
|
|||
|
*EntriesRead = Context.EntriesRead;
|
|||
|
*TotalEntries = Context.TotalEntries;
|
|||
|
*TotalBytesNeeded = Context.TotalBytesNeeded;
|
|||
|
|
|||
|
// Copy the fixed data
|
|||
|
RtlCopyMemory( OutputBuffer,
|
|||
|
TempOutputBuffer,
|
|||
|
(ULONG)(((LPBYTE)Context.NextOutputBuffer)-((LPBYTE)Context.OutputBuffer)) );
|
|||
|
|
|||
|
// Copy the strings
|
|||
|
RtlCopyMemory( ((LPBYTE)OutputBuffer)+(ULONG)(((LPBYTE)Context.OutputBufferEnd)-((LPBYTE)Context.OutputBuffer)),
|
|||
|
Context.OutputBufferEnd,
|
|||
|
(ULONG)(((LPBYTE)OutputBufferEnd)-((LPBYTE)Context.OutputBufferEnd)) );
|
|||
|
|
|||
|
if (*EntriesRead == *TotalEntries) {
|
|||
|
try_return(Status = STATUS_SUCCESS);
|
|||
|
} else {
|
|||
|
try_return(Status = STATUS_MORE_ENTRIES);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
try_exit:NOTHING;
|
|||
|
} except (BR_EXCEPTION) {
|
|||
|
|
|||
|
Status = GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
if (TempOutputBuffer != NULL ) {
|
|||
|
FREE_POOL(TempOutputBuffer);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
EnumerateNamesTransportWorker(
|
|||
|
IN PTRANSPORT Transport,
|
|||
|
IN OUT PVOID Ctx
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the worker routine for BowserEnumerateNamesInDomain.
|
|||
|
|
|||
|
This routine is executed for each transport in the domain.
|
|||
|
It simply calls EnumerateNamesTransportNameWorker for each transport name on the
|
|||
|
transport.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Transport - Transport whose names are to be added to the context.
|
|||
|
|
|||
|
Ctx - Cumulative list of names.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
Status = BowserForEachTransportName( Transport, EnumerateNamesTransportNameWorker, Ctx);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
EnumerateNamesTransportNameWorker(
|
|||
|
IN PTRANSPORT_NAME TransportName,
|
|||
|
IN OUT PVOID Ctx
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the worker routine for EnumerateNamesTransportWorker.
|
|||
|
|
|||
|
It is called for each of the transport name for each transport in the domain.
|
|||
|
It returns that name (supressing duplicates) in the buffer described in the context.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TransportName - Transport name to be added to the context.
|
|||
|
|
|||
|
Ctx - Cumulative list of names.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PENUM_NAMES_CONTEXT Context = Ctx;
|
|||
|
PBOWSER_NAME Name = TransportName->PagedTransportName->Name;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Skip Nameless transports
|
|||
|
//
|
|||
|
if ( Name->Name.Length == 0) {
|
|||
|
// Adding an empty name to the list can result w/ AV
|
|||
|
// on client end (see bug 377078).
|
|||
|
return ( STATUS_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if this name has been packed yet.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
for ( i=0; i<Context->EntriesRead; i++ ) {
|
|||
|
|
|||
|
if ( Name->NameType == Context->OutputBuffer[i].Type ) {
|
|||
|
UNICODE_STRING RelocatedString = Context->OutputBuffer[i].DGReceiverName;
|
|||
|
|
|||
|
RelocatedString.Buffer = (LPWSTR)
|
|||
|
((LPBYTE)RelocatedString.Buffer + Context->OutputBufferDisplacement);
|
|||
|
|
|||
|
if ( RtlEqualUnicodeString( &RelocatedString, &Name->Name, FALSE ) ) {
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This names hasn;t been packed yet,
|
|||
|
// pack it.
|
|||
|
//
|
|||
|
|
|||
|
Context->TotalEntries += 1;
|
|||
|
|
|||
|
if ((ULONG_PTR)Context->OutputBufferEnd - (ULONG_PTR)Context->NextOutputBuffer >
|
|||
|
sizeof(DGRECEIVE_NAMES)+Name->Name.Length) {
|
|||
|
|
|||
|
PDGRECEIVE_NAMES NameEntry = Context->NextOutputBuffer;
|
|||
|
|
|||
|
Context->NextOutputBuffer += 1;
|
|||
|
Context->EntriesRead += 1;
|
|||
|
|
|||
|
NameEntry->DGReceiverName = Name->Name;
|
|||
|
|
|||
|
BowserPackNtString( &NameEntry->DGReceiverName,
|
|||
|
Context->OutputBufferDisplacement,
|
|||
|
(PCHAR)Context->NextOutputBuffer,
|
|||
|
(PCHAR *)&Context->OutputBufferEnd
|
|||
|
);
|
|||
|
|
|||
|
NameEntry->Type = Name->NameType;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Context->TotalBytesNeeded += sizeof(DGRECEIVE_NAMES)+Name->Name.Length;
|
|||
|
|
|||
|
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
BowserpInitializeNames(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
InitializeListHead(&BowserNameHead);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
BowserpUninitializeNames(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
ASSERT (IsListEmpty(&BowserNameHead));
|
|||
|
|
|||
|
return;
|
|||
|
}
|