|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
fspinit.c
Abstract:
This module implements the initialization phase of the LAN Manager server File System Process.
Author:
Chuck Lenzmeier (chuckl) 22-Sep-1989 David Treadwell (davidtr)
Revision History:
--*/
#include "precomp.h"
#include "fspinit.tmh"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_FSPINIT
//
// Forward declarations.
//
PIRP DequeueConfigurationIrp ( VOID );
STATIC NTSTATUS InitializeServer ( VOID );
STATIC NTSTATUS TerminateServer ( VOID );
VOID SrvFreeRegTables ( VOID );
VOID SrvGetRegTables ( VOID );
#if SRVNTVERCHK
VOID SrvGetRegClientNumber ( VOID ); #endif
VOID StartQueueDepthComputations( PWORK_QUEUE queue );
VOID StopQueueDepthComputations( PWORK_QUEUE queue );
VOID ComputeAvgQueueDepth ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 );
BOOLEAN GenerateCrcTable();
BOOLEAN CleanupCrcTable();
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvConfigurationThread )
#pragma alloc_text( PAGE, InitializeServer )
#pragma alloc_text( PAGE, TerminateServer )
#pragma alloc_text( PAGE, SrvFreeRegTables )
#pragma alloc_text( PAGE, SrvGetRegTables )
#if SRVNTVERCHK
#pragma alloc_text( PAGE, SrvGetRegClientNumber )
#endif
#pragma alloc_text( PAGE, DequeueConfigurationIrp )
#pragma alloc_text( PAGE, StartQueueDepthComputations )
#endif
VOID SrvConfigurationThread ( IN PDEVICE_OBJECT pDevice, IN PIO_WORKITEM pWorkItem )
/*++
Routine Description:
This routine processes configuration IRPs.
Arguments:
None.
Return Value:
None.
--*/
{ NTSTATUS status; PIRP irp; PIO_STACK_LOCATION irpSp; ULONG code;
PAGED_CODE( );
IF_DEBUG(FSP1) KdPrint(( "SrvConfigurationThread entered\n" ));
//
// Loop processing requests.
//
while ( TRUE ) {
irp = DequeueConfigurationIrp( );
if ( irp == NULL ) break;
ASSERT( (LONG)SrvConfigurationIrpsInProgress >= 1 );
//
// Get the IRP stack pointer.
//
irpSp = IoGetCurrentIrpStackLocation( irp );
if( irpSp->MajorFunction == IRP_MJ_CLOSE ) {
//
// If the dispatcher routed this irp here, it means
// that we unexpectededly got the last handle close without
// having gotten cleanly terminated first. Ok, so we should
// shut ourselves down, since we can't sensibly run without
// our usermode counterpart.
//
ACQUIRE_LOCK( &SrvStartupShutdownLock ); status = TerminateServer(); RELEASE_LOCK( &SrvStartupShutdownLock );
} else {
ASSERT( irpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL );
try {
//
// Dispatch on the FsControlCode.
//
code = irpSp->Parameters.FileSystemControl.FsControlCode;
switch ( code ) {
case FSCTL_SRV_STARTUP: ACQUIRE_LOCK( &SrvStartupShutdownLock );
status = InitializeServer();
if ( !NT_SUCCESS(status) ) {
//
// Terminate the server FSP.
//
(void)TerminateServer();
}
RELEASE_LOCK( &SrvStartupShutdownLock );
break;
case FSCTL_SRV_SHUTDOWN:
ACQUIRE_LOCK( &SrvStartupShutdownLock ); status = TerminateServer(); RELEASE_LOCK( &SrvStartupShutdownLock );
//
// If there is more than one handle open to the server
// device (i.e., any handles other than the server service's
// handle), return a special status code to the caller (who
// should be the server service). This tells the caller to
// NOT unload the driver, in order prevent weird situations
// where the driver is sort of unloaded, so it can't be used
// but also can't be reloaded, thus preventing the server
// from being restarted.
//
if( NT_SUCCESS( status ) && SrvOpenCount != 1 ) { status = STATUS_SERVER_HAS_OPEN_HANDLES; }
break;
case FSCTL_SRV_REGISTRY_CHANGE: //
// The Parameters section of the server service registry has changed.
// That's likely due to somebody changing the Null Session pipe or
// share lists. Pick up the new settings.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
SrvFreeRegTables(); SrvGetRegTables(); #if SRVNTVERCHK
SrvGetRegClientNumber(); #endif
RELEASE_LOCK( &SrvConfigurationLock );
status = STATUS_SUCCESS;
break;
case FSCTL_SRV_BEGIN_PNP_NOTIFICATIONS: //
// If somebody tries to shut down the server while
// we are registering our handlers, block them until
// we are finished.
//
ACQUIRE_LOCK( &SrvStartupShutdownLock );
{ TDI_CLIENT_INTERFACE_INFO ClientInterfaceInfo;
RtlZeroMemory(&ClientInterfaceInfo, sizeof(TDI_CLIENT_INTERFACE_INFO));
ClientInterfaceInfo.MajorTdiVersion = 2; ClientInterfaceInfo.MinorTdiVersion = 0; ClientInterfaceInfo.ClientName = &StrRegSrvPnpClientName; ClientInterfaceInfo.BindingHandler = SrvPnpBindingHandler; ClientInterfaceInfo.PnPPowerHandler = SrvPnpPowerHandler;
status = TdiRegisterPnPHandlers( &ClientInterfaceInfo, sizeof( ClientInterfaceInfo ), &SrvTdiNotificationHandle );
if (status != STATUS_SUCCESS) { SrvTdiNotificationHandle = NULL; } }
RELEASE_LOCK( &SrvStartupShutdownLock );
if( !NT_SUCCESS( status ) ) {
IF_DEBUG( PNP ) { KdPrint(("TdiRegisterNotificationHandler: status %X\n", status )); }
SrvLogServiceFailure( SRV_SVC_PNP_TDI_NOTIFICATION, status ); }
//
// Allow the transports to begin receiving connections
//
SrvCompletedPNPRegistration = TRUE;
break;
case FSCTL_SRV_XACTSRV_CONNECT: { ANSI_STRING ansiPortName; UNICODE_STRING portName;
IF_DEBUG(XACTSRV) { KdPrint(( "SrvFspConfigurationThread: XACTSRV FSCTL " "received.\n" )); }
ansiPortName.Buffer = irp->AssociatedIrp.SystemBuffer; ansiPortName.Length = (USHORT)irpSp->Parameters.FileSystemControl.InputBufferLength;
status = RtlAnsiStringToUnicodeString( &portName, &ansiPortName, TRUE ); if ( NT_SUCCESS(status) ) { status = SrvXsConnect( &portName ); RtlFreeUnicodeString( &portName ); }
break; }
case FSCTL_SRV_XACTSRV_DISCONNECT: { //
// This is now obsolete
//
status = STATUS_SUCCESS;
break; }
case FSCTL_SRV_START_SMBTRACE: { KdPrint(( "SrvFspConfigurationThread: START_SMBTRACE FSCTL " "received.\n" ));
//
// Initialize the SmbTrace related events.
//
status = SmbTraceInitialize( SMBTRACE_SERVER );
if ( NT_SUCCESS(status) ) {
//
// Create shared memory, create events, start SmbTrace thread,
// and indicate that this is the server.
//
status = SmbTraceStart( irpSp->Parameters.FileSystemControl.InputBufferLength, irpSp->Parameters.FileSystemControl.OutputBufferLength, irp->AssociatedIrp.SystemBuffer, irpSp->FileObject, SMBTRACE_SERVER );
if ( NT_SUCCESS(status) ) {
//
// Record the length of the return information, which is
// simply the length of the output buffer, validated by
// SmbTraceStart.
//
irp->IoStatus.Information = irpSp->Parameters.FileSystemControl.OutputBufferLength;
}
}
break; }
case FSCTL_SRV_SEND_DATAGRAM: { ANSI_STRING domain; ULONG buffer1Length; PVOID buffer2; PSERVER_REQUEST_PACKET srp;
buffer1Length = ALIGN_UP( irpSp->Parameters.FileSystemControl.InputBufferLength, PVOID );
buffer2 = (PCHAR)irp->AssociatedIrp.SystemBuffer + buffer1Length;
srp = irp->AssociatedIrp.SystemBuffer;
//
// Send the second-class mailslot in Buffer2 to the domain
// specified in srp->Name1 on transport specified by srp->Name2.
//
domain = *((PANSI_STRING) &srp->Name1);
status = SrvSendDatagram( &domain, ( srp->Name2.Length != 0 ? &srp->Name2 : NULL ), buffer2, irpSp->Parameters.FileSystemControl.OutputBufferLength );
ExFreePool( irp->AssociatedIrp.SystemBuffer ); DEBUG irp->AssociatedIrp.SystemBuffer = NULL;
break; }
case FSCTL_SRV_NET_FILE_CLOSE: case FSCTL_SRV_NET_SERVER_XPORT_ADD: case FSCTL_SRV_NET_SERVER_XPORT_DEL: case FSCTL_SRV_NET_SESSION_DEL: case FSCTL_SRV_NET_SHARE_ADD: case FSCTL_SRV_NET_SHARE_DEL: { PSERVER_REQUEST_PACKET srp; PVOID buffer2; ULONG buffer1Length; ULONG buffer2Length;
//
// These APIs are handled in the server FSP because they
// open or close FSP handles.
//
ACQUIRE_LOCK_SHARED( &SrvConfigurationLock ); if( SrvFspTransitioning == TRUE && SrvFspActive == TRUE ) { //
// The server is coming down. Do not allow these
// irps to continue.
//
RELEASE_LOCK( &SrvConfigurationLock ); status = STATUS_SERVER_NOT_STARTED; break; } RELEASE_LOCK( &SrvConfigurationLock );
//
// Get the server request packet and secondary input buffer
// pointers.
//
buffer1Length = ALIGN_UP( irpSp->Parameters.FileSystemControl.InputBufferLength, PVOID );
buffer2Length = irpSp->Parameters.FileSystemControl.OutputBufferLength;
srp = irp->AssociatedIrp.SystemBuffer; buffer2 = (PCHAR)srp + buffer1Length;
//
// Dispatch the API request to the appripriate API processing
// routine.
//
status = SrvApiDispatchTable[ SRV_API_INDEX(code) ]( srp, buffer2, buffer2Length );
break; }
default: IF_DEBUG(ERRORS) { KdPrint(( "SrvFspConfigurationThread: Invalid control code %lx\n", irpSp->Parameters.FileSystemControl.FsControlCode )); }
status = STATUS_INVALID_PARAMETER; } } except(EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } }
//
// Make sure we're still at PASSIVE_LEVEL
//
if( KeGetCurrentIrql() > PASSIVE_LEVEL ) { goto bad_irql_failure; }
//
// Complete the IO request.
//
irp->IoStatus.Status = status; IoCompleteRequest( irp, 2 );
//
// Make sure we're still at PASSIVE_LEVEL
//
if( KeGetCurrentIrql() > PASSIVE_LEVEL ) { goto bad_irql_failure; }
ASSERT( (LONG)SrvConfigurationIrpsInProgress >= 0 );
// Make sure we don't continue if there are no IRP's left
if( InterlockedDecrement( (PLONG)&SrvConfigurationIrpsInProgress ) == 0 ) { break; } }
IoFreeWorkItem( pWorkItem );
return;
bad_irql_failure:
DbgPrint( "ERROR: SrvConfigurationThread returning at >PASSIVE level\n" ); DbgBreakPoint();
IoFreeWorkItem( pWorkItem );
return;
} // SrvConfigurationThread
PIRP DequeueConfigurationIrp ( VOID )
/*++
Routine Description:
This routine retrieves an IRP from the configuration work queue.
Arguments:
None.
Return Value:
PIRP - Pointer to configuration IRP, or NULL.
--*/
{ PLIST_ENTRY listEntry; PIRP irp;
PAGED_CODE( );
//
// Take an IRP off the configuration queue.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
listEntry = RemoveHeadList( &SrvConfigurationWorkQueue );
if ( listEntry == &SrvConfigurationWorkQueue ) {
//
// The queue is empty.
//
irp = NULL;
} else {
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
}
RELEASE_LOCK( &SrvConfigurationLock );
return irp;
} // DequeueConfigurationIrp
STATIC NTSTATUS InitializeServer ( VOID )
/*++
Routine Description:
This routine initializes the server.
Arguments:
None.
Return Value:
None.
--*/
{ NTSTATUS status; CLONG i; PWORK_CONTEXT workContext; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; OBJECT_HANDLE_INFORMATION handleInformation; PSID AdminSid; PSID AnonymousSid; PACL Acl; ULONG length; SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; PWORK_QUEUE queue; HANDLE handle; UNICODE_STRING string;
PAGED_CODE();
//
// If running as an Advanced Server, lock all pageable server code.
//
if ( SrvProductTypeServer ) { for ( i = 0; i < SRV_CODE_SECTION_MAX; i++ ) { SrvReferenceUnlockableCodeSection( i ); } }
//
// Initialize the server start time
//
KeQuerySystemTime( &SrvStatistics.StatisticsStartTime );
//
// Get actual alert service name using the display name found in the
// registry.
//
SrvGetAlertServiceName( );
//
// Get the Os versions strings.
//
SrvGetOsVersionString( );
//
// Get the list of null session pipes and shares
//
SrvGetRegTables( );
#if SRVNTVERCHK
SrvGetRegClientNumber(); #endif
#if MULTIPROCESSOR
//
// Allocate and init the nonblocking work queues, paying attention to cache lines
//
i = SrvNumberOfProcessors * sizeof( *SrvWorkQueues ); i += CACHE_LINE_SIZE; SrvWorkQueuesBase = ALLOCATE_NONPAGED_POOL( i, BlockTypeWorkQueue );
if( SrvWorkQueuesBase == NULL ) { return STATUS_INSUFF_SERVER_RESOURCES; }
//
// Round up the start of the work queue data structure to
// the next cache line boundry
//
SrvWorkQueues = (PWORK_QUEUE)(((ULONG_PTR)SrvWorkQueuesBase + CACHE_LINE_SIZE-1) & ~(CACHE_LINE_SIZE-1)); #endif
eSrvWorkQueues = SrvWorkQueues + SrvNumberOfProcessors;
RtlZeroMemory( SrvWorkQueues, (char *)eSrvWorkQueues - (char *)SrvWorkQueues );
for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) { KeInitializeQueue( &queue->Queue, 1 ); queue->WaitMode = SrvProductTypeServer ? KernelMode : UserMode; queue->MaxThreads = SrvMaxThreadsPerQueue; queue->MaximumWorkItems = SrvMaxReceiveWorkItemCount / SrvNumberOfProcessors; queue->MinFreeWorkItems = SrvMinReceiveQueueLength / SrvNumberOfProcessors; queue->MaxFreeRfcbs = SrvMaxFreeRfcbs; queue->MaxFreeMfcbs = SrvMaxFreeMfcbs; ExInitializeSListHead(&queue->InitialWorkItemList); ExInitializeSListHead(&queue->NormalWorkItemList); ExInitializeSListHead(&queue->RawModeWorkItemList); ExInitializeSListHead(&queue->RfcbFreeList); ExInitializeSListHead(&queue->MfcbFreeList); queue->PagedPoolLookAsideList.MaxSize = SrvMaxPagedPoolChunkSize; queue->NonPagedPoolLookAsideList.MaxSize = SrvMaxNonPagedPoolChunkSize; queue->CreateMoreWorkItems.CurrentWorkQueue = queue; queue->CreateMoreWorkItems.BlockHeader.ReferenceCount = 1; queue->IdleTimeOut.QuadPart = SrvIdleThreadTimeOut;
INITIALIZE_SPIN_LOCK( &queue->SpinLock ); SET_SERVER_TIME( queue );
#if MULTIPROCESSOR
StartQueueDepthComputations( queue ); #endif
}
RtlZeroMemory( &SrvDoSWorkItem, sizeof(SPECIAL_WORK_ITEM) ); SrvDoSWorkItem.BlockHeader.ReferenceCount = 1; SrvDoSWorkItemTearDown = SRV_DOS_TEARDOWN_MIN; KeInitializeSpinLock( &SrvDosSpinLock );
//
// Init the Blocking work queue
//
RtlZeroMemory( &SrvBlockingWorkQueue, sizeof( SrvBlockingWorkQueue ) );
KeInitializeQueue( &SrvBlockingWorkQueue.Queue, 0 );
SrvBlockingWorkQueue.WaitMode = SrvProductTypeServer ? KernelMode : UserMode;
SrvBlockingWorkQueue.MaxThreads = SrvMaxThreadsPerQueue; SrvBlockingWorkQueue.IdleTimeOut.QuadPart = SrvIdleThreadTimeOut;
//
// If we are a multiprocessor server system, increase the number of blocking worker threads.
// Since most file opens end up being handled by the blocking queue, this can significantly
// improve performance for open-intensive workloads.
//
if( SrvProductTypeServer == TRUE && SrvNumberOfProcessors > 1 ) { SrvBlockingWorkQueue.MaxThreads *= 2; }
SET_SERVER_TIME( &SrvBlockingWorkQueue );
//
// Build the receive work item list.
//
status = SrvAllocateInitialWorkItems( ); if ( !NT_SUCCESS(status) ) { return status; }
//
// Build the raw mode work item list, and spread it around
// the processors
//
queue = SrvWorkQueues; for ( i = 0; i < SrvInitialRawModeWorkItemCount; i++ ) {
SrvAllocateRawModeWorkItem( &workContext, queue );
if ( workContext == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; }
GET_SERVER_TIME( queue, &workContext->Timestamp );
SrvRequeueRawModeWorkItem( workContext );
if( ++queue == eSrvWorkQueues ) queue = SrvWorkQueues; }
//
// Create worker threads.
//
status = SrvCreateWorkerThreads( ); if ( !NT_SUCCESS(status) ) { return status; }
//
// Initialize the scavenger.
//
status = SrvInitializeScavenger( ); if ( !NT_SUCCESS(status) ) { return status; }
//
// Initialize the global ordered lists.
//
// *** WARNING: Be careful when changing the locks associated with
// these ordered lists. Certain places in the code depend on
// the level of the lock associated with a list. Examples
// include (but are NOT limited to) SrvSmbSessionSetupAndX,
// SrvSmbTreeConnect, SrvSmbTreeConnectAndX, and CompleteOpen.
//
SrvInitializeOrderedList( &SrvEndpointList, FIELD_OFFSET( ENDPOINT, GlobalEndpointListEntry ), SrvCheckAndReferenceEndpoint, SrvDereferenceEndpoint, &SrvEndpointLock );
SrvInitializeOrderedList( &SrvRfcbList, FIELD_OFFSET( RFCB, GlobalRfcbListEntry ), SrvCheckAndReferenceRfcb, SrvDereferenceRfcb, &SrvOrderedListLock );
SrvInitializeOrderedList( &SrvSessionList, FIELD_OFFSET( SESSION, GlobalSessionListEntry ), SrvCheckAndReferenceSession, SrvDereferenceSession, &SrvOrderedListLock );
SrvInitializeOrderedList( &SrvTreeConnectList, FIELD_OFFSET( TREE_CONNECT, GlobalTreeConnectListEntry ), SrvCheckAndReferenceTreeConnect, SrvDereferenceTreeConnect, &SrvShareLock );
//
// Open handle to NPFS. Do not return an error if we fail so that
// the server can still run without NPFS in the system.
//
SrvInitializeObjectAttributes_U( &objectAttributes, &SrvNamedPipeRootDirectory, 0, NULL, NULL );
status = IoCreateFile( &SrvNamedPipeHandle, GENERIC_READ | GENERIC_WRITE, &objectAttributes, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, // Create Options
NULL, // EA Buffer
0, // EA Length
CreateFileTypeNone, // File type
NULL, // ExtraCreateParameters
IO_FORCE_ACCESS_CHECK // Options
);
if (!NT_SUCCESS(status)) {
INTERNAL_ERROR ( ERROR_LEVEL_EXPECTED, "InitializeServer: Failed to open NPFS, err=%X\n", status, NULL );
SrvLogServiceFailure( SRV_SVC_IO_CREATE_FILE_NPFS, status ); SrvNamedPipeHandle = NULL; return status;
} else {
//
// Get a pointer to the NPFS device object
//
status = SrvVerifyDeviceStackSize( SrvNamedPipeHandle, TRUE, &SrvNamedPipeFileObject, &SrvNamedPipeDeviceObject, &handleInformation );
if ( !NT_SUCCESS( status )) {
INTERNAL_ERROR( ERROR_LEVEL_EXPECTED, "InitializeServer: Verify Device Stack Size failed: %X\n", status, NULL );
SrvNtClose( SrvNamedPipeHandle, FALSE ); SrvNamedPipeHandle = NULL; return status; } }
//
// Initialize Dfs operations
//
SrvInitializeDfs();
//
// Intialize SrvAdminSecurityDescriptor, which allows Administrators READ access.
// This descriptor is used by the server to check if a user is an administrator
// in SrvIsAdmin().
status = RtlCreateSecurityDescriptor( &SrvAdminSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if( !NT_SUCCESS( status ) ) { return status; }
//
// Create an admin SID
//
AdminSid = ALLOCATE_HEAP_COLD( RtlLengthRequiredSid( 2 ), BlockTypeAdminCheck ); if( AdminSid == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; }
RtlInitializeSid( AdminSid, &BuiltinAuthority, (UCHAR)2 ); *(RtlSubAuthoritySid( AdminSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; *(RtlSubAuthoritySid( AdminSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
length = sizeof(ACL) + sizeof( ACCESS_ALLOWED_ACE ) + RtlLengthSid( AdminSid ); Acl = ALLOCATE_HEAP_COLD( length, BlockTypeAdminCheck ); if( Acl == NULL ) { FREE_HEAP( AdminSid ); return STATUS_INSUFFICIENT_RESOURCES; }
status = RtlCreateAcl( Acl, length, ACL_REVISION2 );
if( NT_SUCCESS( status ) ) { status = RtlAddAccessAllowedAce( Acl, ACL_REVISION2, FILE_GENERIC_READ, AdminSid ); }
if( NT_SUCCESS( status ) ) { status = RtlSetDaclSecurityDescriptor( &SrvAdminSecurityDescriptor, TRUE, Acl, FALSE ); }
if( NT_SUCCESS( status ) ) { status = RtlSetOwnerSecurityDescriptor( &SrvAdminSecurityDescriptor, AdminSid, FALSE ); }
if( !NT_SUCCESS( status ) ) { return status; }
//
// Intialize SrvNullSessionSecurityDescriptor, which allows anonymous
// logons READ access. This descriptor is used by the server to check
// if a user is an null session in SrvIsNullSession().
//
status = RtlCreateSecurityDescriptor( &SrvNullSessionSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if( !NT_SUCCESS( status ) ) { return status; }
//
// Create an anonymous SID
//
AnonymousSid = ALLOCATE_HEAP_COLD( RtlLengthRequiredSid( 1 ), BlockTypeAdminCheck ); if( AnonymousSid == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; }
RtlInitializeSid( AnonymousSid, &BuiltinAuthority, (UCHAR)1 ); *(RtlSubAuthoritySid( AnonymousSid, 0 )) = SECURITY_ANONYMOUS_LOGON_RID;
length = sizeof(ACL) + sizeof( ACCESS_ALLOWED_ACE ) + RtlLengthSid( AnonymousSid ); Acl = ALLOCATE_HEAP_COLD( length, BlockTypeAdminCheck ); if( Acl == NULL ) { FREE_HEAP( AnonymousSid ); return STATUS_INSUFFICIENT_RESOURCES; }
status = RtlCreateAcl( Acl, length, ACL_REVISION2 );
if( NT_SUCCESS( status ) ) { status = RtlAddAccessAllowedAce( Acl, ACL_REVISION2, FILE_GENERIC_READ, AnonymousSid ); }
if( NT_SUCCESS( status ) ) { status = RtlSetDaclSecurityDescriptor( &SrvNullSessionSecurityDescriptor, TRUE, Acl, FALSE ); }
if( NT_SUCCESS( status ) ) { status = RtlSetOwnerSecurityDescriptor( &SrvNullSessionSecurityDescriptor, AnonymousSid, FALSE ); }
if( !NT_SUCCESS( status ) ) { return status; }
(VOID) InitSecurityInterface();
status = SrvValidateUser( &SrvNullSessionToken, NULL, NULL, NULL, StrNullAnsi, 1, NULL, 0, FALSE, NULL );
if ( !NT_SUCCESS(status) ) {
//
// LSA doesn't want to let the null session in. He's the boss!
//
INVALIDATE_SECURITY_HANDLE( SrvNullSessionToken ); }
//
// See if the filesystems are allowing extended characters in 8.3 names. If
// so, we need to filter them out ourself.
//
RtlInitUnicodeString( &string, StrRegExtendedCharsInPath ); InitializeObjectAttributes( &objectAttributes, &string, OBJ_CASE_INSENSITIVE, NULL, NULL );
status = ZwOpenKey( &handle, KEY_READ, &objectAttributes );
if( NT_SUCCESS( status ) ) { ULONG resultLength; union { KEY_VALUE_FULL_INFORMATION; UCHAR buffer[ sizeof( KEY_VALUE_FULL_INFORMATION ) + 100 ]; } keyValueInformation;
RtlInitUnicodeString( &string, StrRegExtendedCharsInPathValue ); status = ZwQueryValueKey( handle, &string, KeyValueFullInformation, &keyValueInformation, sizeof( keyValueInformation ), &resultLength );
if( NT_SUCCESS( status ) && keyValueInformation.Type == REG_DWORD && keyValueInformation.DataLength != 0 ) {
SrvFilterExtendedCharsInPath = *(PULONG)(((PUCHAR)(&keyValueInformation)) + keyValueInformation.DataOffset) ? TRUE : FALSE; }
ZwClose( handle ); }
//
// Get a handle to use in PoRegisterSystemState() calls
//
SrvPoRegistrationState = PoRegisterSystemState( NULL, 0 );
//
// Indicate that the server is active.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
SrvFspTransitioning = FALSE; SrvFspActive = TRUE;
RELEASE_LOCK( &SrvConfigurationLock );
return STATUS_SUCCESS;
} // InitializeServer
STATIC NTSTATUS TerminateServer ( VOID )
/*++
Routine Description:
This routine terminates the server. The following steps are performed:
- Walk through SrvEndpointList and close all open endpoints.
- Walk through the work context blocks in the work queues getting rid of them as appropiate
- Close all shares open in the server
- Deallocate the search table
Arguments:
Return Value:
None.
--*/
{ PLIST_ENTRY listEntry; PSINGLE_LIST_ENTRY singleListEntry; PENDPOINT endpoint; ULONG numberOfThreads; PWORK_CONTEXT workContext; PSHARE share; ULONG i; SPECIAL_WORK_ITEM WorkItem; PSRV_TIMER timer; PSID adminsid; PSID anonymoussid; PACL acl; BOOLEAN defaulted; BOOLEAN daclpresent; NTSTATUS status; PWORK_QUEUE queue; PIRP irp; PLIST_ENTRY listEntryRoot;
PAGED_CODE( );
IF_DEBUG(FSP1) KdPrint(( "LAN Manager server FSP terminating.\n" ));
//
// Do not receive PNP notifications anymore
//
if( SrvTdiNotificationHandle != NULL ) {
status = TdiDeregisterPnPHandlers( SrvTdiNotificationHandle );
if( !NT_SUCCESS( status ) ) { KdPrint(( "TdiDeregisterPnPHandlers status %X\n", status )); SrvLogServiceFailure( SRV_SVC_PNP_TDI_NOTIFICATION, status ); return status; }
SrvTdiNotificationHandle = NULL; }
//
// Make sure we are not processing any other configuration IRPs. We know
// that no new configuration IRPs can enter the queue because SrvFspTransitioning
// has been set.
//
// First drain the configuration queue
//
while( 1 ) {
ACQUIRE_LOCK( &SrvConfigurationLock );
irp = DequeueConfigurationIrp( );
RELEASE_LOCK( &SrvConfigurationLock );
if( irp == NULL ) { break; }
irp->IoStatus.Status = STATUS_SERVER_NOT_STARTED; IoCompleteRequest( irp, 2 ); InterlockedDecrement( (PLONG)&SrvConfigurationIrpsInProgress ); }
//
// Now wait until any already dequeued configuration IRPs have been completed. We
// check for >1 because we need to account for our own IRP
//
while( SrvConfigurationIrpsInProgress > 1 ) {
LARGE_INTEGER interval;
interval.QuadPart = -1*10*1000*10; // .01 second
ASSERT( (LONG)SrvConfigurationIrpsInProgress > 0 );
KeDelayExecutionThread( KernelMode, FALSE, &interval ); }
//
// If there are outstanding API requests in the server FSD,
// wait for them to complete. The last one to complete will
// set SrvApiCompletionEvent.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
if ( SrvApiRequestCount != 0 ) {
//
// We must release the lock before waiting so that the FSD
// threads can get it to decrement SrvApiRequestCount.
//
RELEASE_LOCK( &SrvConfigurationLock );
for (;;) { NTSTATUS WaitStatus;
//
// Wait until the last API has completed. Since
// SrvFspTransitioning was set to TRUE earlier, we know that the
// API that makes SrvApiRequestCount go to zero will set the
// event.
//
// This wait allows us to make the assumption later on that no
// other thread is operating on server data structures.
//
WaitStatus = KeWaitForSingleObject( &SrvApiCompletionEvent, UserRequest, UserMode, // let kernel stack be paged
FALSE, NULL );
if (WaitStatus != STATUS_USER_APC) { break; } }
} else {
RELEASE_LOCK( &SrvConfigurationLock ); }
//
// Close all the endpoints opened by the server. This also results
// in the connections, sessions, tree connects, and files opened
// by the server being closed.
//
ACQUIRE_LOCK( &SrvEndpointLock );
if ( SrvEndpointCount != 0 ) {
listEntry = SrvEndpointList.ListHead.Flink;
while ( listEntry != &SrvEndpointList.ListHead ) {
endpoint = CONTAINING_RECORD( listEntry, ENDPOINT, GlobalEndpointListEntry );
if ( GET_BLOCK_STATE(endpoint) != BlockStateActive ) { listEntry = listEntry->Flink; continue; }
//
// We don't want to hold the endpoint lock while we close
// the endpoint (this causes lock level problems), so we have
// to play some games.
//
// Reference the endpoint to ensure that it doesn't go away.
// (We'll need its Flink later.) Close the endpoint. This
// releases the endpoint lock. Reacquire the endpoint lock.
// Capture the address of the next endpoint. Dereference the
// current endpoint.
//
SrvReferenceEndpoint( endpoint ); SrvCloseEndpoint( endpoint );
ACQUIRE_LOCK( &SrvEndpointLock );
listEntry = listEntry->Flink; SrvDereferenceEndpoint( endpoint );
}
RELEASE_LOCK( &SrvEndpointLock );
for (;;) { NTSTATUS WaitStatus;
//
// Wait until all the endpoints have actually closed.
//
WaitStatus = KeWaitForSingleObject( &SrvEndpointEvent, UserRequest, UserMode, // let kernel stack be paged
FALSE, NULL );
if (WaitStatus != STATUS_USER_APC) { break; } }
} else {
RELEASE_LOCK( &SrvEndpointLock );
}
KeClearEvent( &SrvEndpointEvent );
//
// All the endpoints are closed, so it's impossible for there to
// be any outstanding requests to xactsrv. So shut it down.
//
SrvXsDisconnect();
//
// Queue a special work item to each of the work queues. This
// work item, when received by a worker thread. causes the thread
// to requeue the work item and terminate itself. In this way,
// each of the worker threads receives the work item and kills
// itself.
//
WorkItem.FspRestartRoutine = SrvTerminateWorkerThread; SET_BLOCK_TYPE( &WorkItem, BlockTypeWorkContextSpecial );
//
// Kill the threads on the nonblocking work queues
//
if ( SrvWorkQueues != NULL ) {
for( queue=SrvWorkQueues; queue && queue < eSrvWorkQueues; queue++ ) {
WorkItem.CurrentWorkQueue = queue;
SrvInsertWorkQueueTail( queue, (PQUEUEABLE_BLOCK_HEADER)&WorkItem );
//
// Wait for the threads to all die
//
while( queue->Threads != 0 ) {
LARGE_INTEGER interval;
interval.QuadPart = -1*10*1000*10; // .01 second
KeDelayExecutionThread( KernelMode, FALSE, &interval ); }
KeRundownQueue( &queue->Queue ); }
//
// Kill the threads on the blocking work queues
//
WorkItem.CurrentWorkQueue = &SrvBlockingWorkQueue;
SrvInsertWorkQueueTail( &SrvBlockingWorkQueue, (PQUEUEABLE_BLOCK_HEADER)&WorkItem );
//
// Wait for the threads to all die
//
while( SrvBlockingWorkQueue.Threads != 0 ) {
LARGE_INTEGER interval;
interval.QuadPart = -1*10*1000*10; // .01 second
KeDelayExecutionThread( KernelMode, FALSE, &interval ); }
KeRundownQueue( &SrvBlockingWorkQueue.Queue ); }
//
// Free any space allocated for the Null Session pipe and share lists
//
SrvFreeRegTables();
//
// If we allocated memory for the os version strings, free it now.
//
if ( SrvNativeOS.Buffer != NULL && SrvNativeOS.Buffer != StrDefaultNativeOs ) {
FREE_HEAP( SrvNativeOS.Buffer ); SrvNativeOS.Buffer = NULL;
RtlFreeOemString( &SrvOemNativeOS ); SrvOemNativeOS.Buffer = NULL; }
//
// If allocated memory for the display name, free it now.
//
if ( SrvAlertServiceName != NULL && SrvAlertServiceName != StrDefaultSrvDisplayName ) {
FREE_HEAP( SrvAlertServiceName ); SrvAlertServiceName = NULL; }
//
// Make sure the scavenger is not running.
//
SrvTerminateScavenger( );
#if MULTIPROCESSOR
if( SrvWorkQueues ) { for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) { StopQueueDepthComputations( queue ); } } #endif
//
// Free the work items in the work queues and the receive work item
// list. This also deallocates the SMB buffers. Note that work
// items allocated dynamically may be deallocated singly, while work
// items allocated at server startup are part of one large block,
// and may not be deallocated singly.
//
// !!! Does this properly clean up buffers allocated during SMB
// processing? Probably not. Should probably allow the worker
// threads to run the work queue normally before they stop.
//
if( SrvWorkQueues ) {
for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) {
//
// Clean out the single FreeContext spot
//
workContext = NULL; workContext = (PWORK_CONTEXT)InterlockedExchangePointer( &queue->FreeContext, workContext );
if( workContext != NULL && workContext->PartOfInitialAllocation == FALSE ) { SrvFreeNormalWorkItem( workContext ); }
//
// Clean out the normal work item list
//
while( 1 ) { singleListEntry = ExInterlockedPopEntrySList( &queue->NormalWorkItemList, &queue->SpinLock ); if( singleListEntry == NULL ) { break; } workContext = CONTAINING_RECORD( singleListEntry, WORK_CONTEXT, SingleListEntry );
SrvFreeNormalWorkItem( workContext ); queue->FreeWorkItems--; }
//
// Clean out the raw mode work item list
//
while( 1 ) { singleListEntry = ExInterlockedPopEntrySList( &queue->RawModeWorkItemList, &queue->SpinLock ); if( singleListEntry == NULL ) { break; }
workContext = CONTAINING_RECORD( singleListEntry, WORK_CONTEXT, SingleListEntry );
SrvFreeRawModeWorkItem( workContext ); }
//
// Free up any saved rfcbs
//
if( queue->CachedFreeRfcb != NULL ) { FREE_HEAP( queue->CachedFreeRfcb->PagedRfcb ); DEALLOCATE_NONPAGED_POOL( queue->CachedFreeRfcb ); queue->CachedFreeRfcb = NULL; }
while( 1 ) { PRFCB Rfcb;
singleListEntry = ExInterlockedPopEntrySList( &queue->RfcbFreeList, &queue->SpinLock ); if( singleListEntry == NULL ) { break; }
Rfcb = CONTAINING_RECORD( singleListEntry, RFCB, SingleListEntry ); FREE_HEAP( Rfcb->PagedRfcb ); DEALLOCATE_NONPAGED_POOL( Rfcb ); }
//
// Free up any saved mfcbs
//
if( queue->CachedFreeMfcb != NULL ) { DEALLOCATE_NONPAGED_POOL( queue->CachedFreeMfcb ); queue->CachedFreeMfcb = NULL; }
while( 1 ) { PNONPAGED_MFCB nonpagedMfcb;
singleListEntry = ExInterlockedPopEntrySList( &queue->MfcbFreeList, &queue->SpinLock ); if( singleListEntry == NULL ) { break; }
nonpagedMfcb = CONTAINING_RECORD( singleListEntry, NONPAGED_MFCB, SingleListEntry );
DEALLOCATE_NONPAGED_POOL( nonpagedMfcb ); } }
} // SrvWorkQueues
//
// All dynamic work items have been freed, and the work item queues
// have been emptied. Release the initial work item allocation.
//
SrvFreeInitialWorkItems( );
//
// Walk through the global share list, closing them all.
//
for( listEntryRoot = SrvShareHashTable; listEntryRoot < &SrvShareHashTable[ NSHARE_HASH_TABLE ]; listEntryRoot++ ) {
while( listEntryRoot->Flink != listEntryRoot ) {
share = CONTAINING_RECORD( listEntryRoot->Flink, SHARE, GlobalShareList );
SrvCloseShare( share ); } }
//
// If we opened the NPFS during initialization, close the handle now
// and dereference the NPFS file object.
//
if ( SrvNamedPipeHandle != NULL) {
SrvNtClose( SrvNamedPipeHandle, FALSE ); ObDereferenceObject( SrvNamedPipeFileObject );
SrvNamedPipeHandle = NULL;
}
//
// Disconnect from the Dfs driver
//
SrvTerminateDfs();
//
// Clean up the Dns Domain Name if necessary
//
if( SrvDnsDomainName ) { DEALLOCATE_NONPAGED_POOL( SrvDnsDomainName ); SrvDnsDomainName = NULL; }
//
// Clean up the admin security descriptor
//
status = RtlGetDaclSecurityDescriptor( &SrvAdminSecurityDescriptor, &daclpresent, &acl, &defaulted ); if( !NT_SUCCESS( status ) || !daclpresent ) { acl = NULL; }
status = RtlGetOwnerSecurityDescriptor( &SrvAdminSecurityDescriptor, &adminsid, &defaulted );
if( NT_SUCCESS( status ) && adminsid != NULL ) { FREE_HEAP( adminsid ); }
if( acl != NULL ) { FREE_HEAP( acl ); }
//
// Clean up the null session security descriptor
//
status = RtlGetDaclSecurityDescriptor( &SrvNullSessionSecurityDescriptor, &daclpresent, &acl, &defaulted ); if( !NT_SUCCESS( status ) || !daclpresent ) { acl = NULL; }
status = RtlGetOwnerSecurityDescriptor( &SrvNullSessionSecurityDescriptor, &anonymoussid, &defaulted );
if( NT_SUCCESS( status ) && anonymoussid != NULL ) { FREE_HEAP( anonymoussid ); }
if( acl != NULL ) { FREE_HEAP( acl ); }
if (!CONTEXT_NULL(SrvNullSessionToken)) { DeleteSecurityContext(&SrvNullSessionToken); INVALIDATE_SECURITY_HANDLE( SrvNullSessionToken ); }
//
// Delete the global ordered lists.
//
SrvDeleteOrderedList( &SrvEndpointList ); SrvDeleteOrderedList( &SrvRfcbList ); SrvDeleteOrderedList( &SrvSessionList ); SrvDeleteOrderedList( &SrvTreeConnectList );
//
// Clear out the timer pool.
//
while ( (singleListEntry = ExInterlockedPopEntrySList( &SrvTimerList, &GLOBAL_SPIN_LOCK(Timer) )) != NULL ) { timer = CONTAINING_RECORD( singleListEntry, SRV_TIMER, Next ); DEALLOCATE_NONPAGED_POOL( timer ); }
if( SrvWorkQueues ) {
//
// Clear out the saved pool chunks
//
for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) { //
// Free up any paged pool that we've saved.
//
SrvClearLookAsideList( &queue->PagedPoolLookAsideList, SrvFreePagedPool );
//
// Free up any nonpaged pool that we've saved.
//
SrvClearLookAsideList( &queue->NonPagedPoolLookAsideList, SrvFreeNonPagedPool ); }
#if MULTIPROCESSOR
DEALLOCATE_NONPAGED_POOL( SrvWorkQueuesBase ); SrvWorkQueuesBase = NULL; SrvWorkQueues = NULL; #endif
}
//
// Unlock pageable sections.
//
for ( i = 0; i < SRV_CODE_SECTION_MAX; i++ ) { if ( SrvSectionInfo[i].Handle != NULL ) { ASSERT( SrvSectionInfo[i].ReferenceCount != 0 ); MmUnlockPagableImageSection( SrvSectionInfo[i].Handle ); SrvSectionInfo[i].Handle = 0; SrvSectionInfo[i].ReferenceCount = 0; } }
//
// Zero out the statistics database.
//
RtlZeroMemory( &SrvStatistics, sizeof(SrvStatistics) ); #if SRVDBG_STATS || SRVDBG_STATS2
RtlZeroMemory( &SrvDbgStatistics, sizeof(SrvDbgStatistics) ); #endif
//
// Free the handle used in PoRegisterSystemState
//
if( SrvPoRegistrationState != NULL ) { PoUnregisterSystemState( SrvPoRegistrationState ); SrvPoRegistrationState = NULL; }
//
// Indicate that the server is no longer active.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
SrvFspTransitioning = FALSE; SrvFspActive = FALSE; SrvSvcProcess = NULL;
RELEASE_LOCK( &SrvConfigurationLock );
IF_DEBUG(FSP1) KdPrint(( "LAN Manager server FSP termination complete.\n" ));
return STATUS_SUCCESS;
} // TerminateServer
VOID SrvFreeRegTables ( VOID ) /*++
Routine Description:
This routine frees space allocated for the list of legal Null session shares and pipes. The SrvConfigurationLock must be held when this routine is called.
Arguments:
None.
Return Value:
None.
--*/ { PAGED_CODE( );
//
// If we allocated a buffer for the list of null session pipes,
// free it now.
//
if ( SrvNullSessionPipes != NULL && SrvNullSessionPipes != StrDefaultNullSessionPipes ) {
FREE_HEAP( SrvNullSessionPipes ); } SrvNullSessionPipes = NULL;
if( SrvNoRemapPipeNames != NULL && SrvNoRemapPipeNames != StrDefaultNoRemapPipeNames ) {
FREE_HEAP( SrvNoRemapPipeNames ); } SrvNoRemapPipeNames = NULL;
if ( SrvPipesNeedLicense != NULL && SrvPipesNeedLicense != StrDefaultPipesNeedLicense ) {
FREE_HEAP( SrvPipesNeedLicense ); } SrvPipesNeedLicense = NULL;
if ( SrvNullSessionShares != NULL && SrvNullSessionShares != StrDefaultNullSessionShares ) {
FREE_HEAP( SrvNullSessionShares ); } SrvNullSessionShares = NULL;
#if SRVNTVERCHK
if( SrvInvalidDomainNames != NULL ) { FREE_HEAP( SrvInvalidDomainNames ); } SrvInvalidDomainNames = NULL; #endif
#if SRVCATCH
if( SrvCatchBuf != NULL ) { FREE_HEAP( SrvCatchBuf ); SrvCatchBuf = NULL; } if( SrvCatchExtBuf != NULL ) { FREE_HEAP( SrvCatchExtBuf ); SrvCatchExtBuf = NULL; } if( SrvCatchShareNames != NULL ) { FREE_HEAP( SrvCatchShareNames ); SrvCatchShareNames = NULL; SrvCatchShares = 0; CleanupCrcTable(); } #endif
}
VOID SrvGetRegTables ( VOID ) /*++
Routine Description:
This routine loads the lists of valid shares and pipes for null sessions. The SrvConfigurationLock must be held when this routine is called.
Arguments:
None.
Return Value:
None.
--*/ { PWSTR *strErrorLogIgnore;
PAGED_CODE( );
//
// Get the list of null session pipes.
//
ASSERT( SrvNullSessionPipes == NULL ); SrvGetMultiSZList( &SrvNullSessionPipes, StrRegSrvParameterPath, StrRegNullSessionPipes, StrDefaultNullSessionPipes );
//
// Get the list of non-remappable pipe names
//
ASSERT( SrvNoRemapPipeNames == NULL ); SrvGetMultiSZList( &SrvNoRemapPipeNames, StrRegSrvParameterPath, StrRegNoRemapPipes, StrDefaultNoRemapPipeNames );
//
// Get the list of pipes requiring licenses
//
ASSERT( SrvPipesNeedLicense == NULL ); SrvGetMultiSZList( &SrvPipesNeedLicense, StrRegSrvParameterPath, StrRegPipesNeedLicense, StrDefaultPipesNeedLicense );
//
// Get the list of null session pipes.
//
ASSERT( SrvNullSessionShares == NULL ); SrvGetMultiSZList( &SrvNullSessionShares, StrRegSrvParameterPath, StrRegNullSessionShares, StrDefaultNullSessionShares );
#if SRVCATCH
{ USHORT i;
SrvCatch.Length = 0; SrvCatch.Buffer = 0; if( SrvCatchBuf != NULL ) { FREE_HEAP( SrvCatchBuf ); SrvCatchBuf = NULL; }
SrvGetMultiSZList( &SrvCatchBuf, StrRegSrvParameterPath, L"CheckFile", 0 );
if( SrvCatchBuf != NULL ) { SrvCatch.Buffer = SrvCatchBuf[0]; for( i = 0; SrvCatch.Buffer[i]; i++ ) ; SrvCatch.Length = i * sizeof( SrvCatch.Buffer[0] ); }
SrvCatchExt.Length = 0; SrvCatchExt.Buffer = 0; if( SrvCatchExtBuf != NULL ) { FREE_HEAP( SrvCatchExtBuf ); SrvCatchExtBuf = NULL; } SrvGetMultiSZList( &SrvCatchExtBuf, StrRegSrvParameterPath, L"CheckExtension", 0 );
if( SrvCatchExtBuf != NULL ) { SrvCatchExt.Buffer = SrvCatchExtBuf[0]; for( i = 0; SrvCatchExt.Buffer[i]; i++ ) ; SrvCatchExt.Length = i * sizeof( SrvCatchExt.Buffer[0] ); } if( SrvCatchShareNames != NULL ) { FREE_HEAP( SrvCatchShareNames ); SrvCatchShareNames = NULL; } SrvGetMultiSZList( &SrvCatchShareNames, StrRegSrvParameterPath, L"CheckShares", 0 ); if( SrvCatchShareNames != NULL ) { for( i=0; SrvCatchShareNames[i]; i++ ) ; SrvCatchShares = i; }
if( SrvCatchShares > 0 ) { if( !GenerateCrcTable() ) { FREE_HEAP( SrvCatchShareNames ); SrvCatchShareNames = NULL; SrvCatchShares = 0; } } } #endif
//
// Get the list of error codes that we don't log
//
SrvGetMultiSZList( &strErrorLogIgnore, StrRegSrvParameterPath, StrRegErrorLogIgnore, StrDefaultErrorLogIgnore );
if( strErrorLogIgnore != NULL ) { DWORD i;
//
// They came in as strings, convert to NTSTATUS codes
//
for( i=0; i < SRVMAXERRLOGIGNORE; i++ ) { NTSTATUS Status; PWSTR p;
if( (p = strErrorLogIgnore[i]) == NULL ) break;
for( Status = 0; *p; p++ ) { if( *p >= L'A' && *p <= L'F' ) { Status <<= 4; Status += 10 + (*p - L'A'); } else if( *p >= '0' && *p <= '9' ) { Status <<= 4; Status += *p - L'0'; } }
SrvErrorLogIgnore[i] = Status;
IF_DEBUG(FSP1) KdPrint(( "LAN Manager server: %X errs not logged\n", Status )); } SrvErrorLogIgnore[i] = 0;
if( strErrorLogIgnore != StrDefaultErrorLogIgnore ) { FREE_HEAP( strErrorLogIgnore ); } }
#if SRVNTVERCHK
//
// Get the list of Domains that we disallow if the client
// is running NT5
//
ASSERT( SrvInvalidDomainNames == NULL ); SrvGetMultiSZList( &SrvInvalidDomainNames, StrRegSrvParameterPath, StrRegInvalidDomainNames, NULL );
if( SrvInvalidDomainNames != NULL ) {
int i;
KdPrint(( "SRV disallows NT5 clients from the following domains:\n" )); for( i = 0; SrvInvalidDomainNames[i]; i++ ) { KdPrint(( " %ws\n", SrvInvalidDomainNames[i] )); } }
//
// Get the list of IP addresses of clients that we will allow to connect
// regardless of build number
//
{ PWSTR *strAllowedIPAddresses; int i;
//
// Wipe out the current list
//
RtlZeroMemory( SrvAllowIPAddress, sizeof( SrvAllowIPAddress ) );
SrvGetMultiSZList( &strAllowedIPAddresses, StrRegSrvParameterPath, StrRegAllowedIPAddresses, NULL );
if( strAllowedIPAddresses != NULL ) {
KdPrint(( "SRV ignores NT build version of clients at following IP addrs:\n" )); //
// Fill it with the new ones
//
for(i = 0; strAllowedIPAddresses[i] && i < (sizeof(SrvAllowIPAddress)/sizeof(SrvAllowIPAddress[0]))-1; i++ ) {
LPWSTR p; DWORD addr = 0; char *s = (char *)&addr;
//
// Convert the IP address to a DWORD and store it
//
for( p = strAllowedIPAddresses[i]; *p && s < ((char *)&addr)+sizeof(addr); p++ ) { if( *p == L'.' ) { s++; } else if( *p >= '0' && *p <= '9' ) { *s = (*s * 10) + (*p - L'0'); } }
SrvAllowIPAddress[i] = addr;
KdPrint(( " %ws\n", strAllowedIPAddresses[i] ));
} FREE_HEAP( strAllowedIPAddresses ); }
} #endif
}
#if SRVNTVERCHK
VOID SrvGetRegClientNumber ( VOID ) /*++
Routine Description:
This routine reads MinNt5Client REG_DWORD from the registry and sets the global SrvMinNT5Client to the retrieved value. Later, if a client running >= NT5 with a build number less than SrvMinNT5Client tries to connect to a disk share, we reject the connection. This mechanism is used on our SLM servers to ensure that people are upgrading to current builds.
--*/ { UNICODE_STRING unicodeKeyName; UNICODE_STRING unicodeParamPath; OBJECT_ATTRIBUTES objAttributes; HANDLE keyHandle; PKEY_VALUE_PARTIAL_INFORMATION infoBuffer; ULONG lengthNeeded; NTSTATUS status;
PAGED_CODE( );
SrvMinNT5Client = 0; SrvMinNT5ClientIPCToo = FALSE;
RtlInitUnicodeString( &unicodeParamPath, StrRegSrvParameterPath ); RtlInitUnicodeString( &unicodeKeyName, L"MinNT5Client" );
InitializeObjectAttributes( &objAttributes, &unicodeParamPath, OBJ_CASE_INSENSITIVE, NULL, NULL );
status = ZwOpenKey( &keyHandle, KEY_QUERY_VALUE, &objAttributes );
if ( NT_SUCCESS(status) ) {
status = ZwQueryValueKey( keyHandle, &unicodeKeyName, KeyValuePartialInformation, NULL, 0, &lengthNeeded );
if( status != STATUS_BUFFER_TOO_SMALL ) {
RtlInitUnicodeString( &unicodeKeyName, L"MinNT5ClientIPC" );
status = ZwQueryValueKey( keyHandle, &unicodeKeyName, KeyValuePartialInformation, NULL, 0, &lengthNeeded );
SrvMinNT5ClientIPCToo = TRUE; }
if( status == STATUS_BUFFER_TOO_SMALL ) {
infoBuffer = ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
if( infoBuffer ) {
status = ZwQueryValueKey( keyHandle, &unicodeKeyName, KeyValuePartialInformation, infoBuffer, lengthNeeded, &lengthNeeded );
if( NT_SUCCESS( status ) && infoBuffer->Type == REG_DWORD && infoBuffer->DataLength == sizeof( DWORD ) ) {
SrvMinNT5Client = *(DWORD *)(&infoBuffer->Data[0]);
KdPrint(( "SRV: Restrict NT5 clients to builds >= %u\n", SrvMinNT5Client ));
if( SrvMinNT5ClientIPCToo ) { KdPrint(( " Restrict IPC clients\n" )); } }
FREE_HEAP( infoBuffer ); } }
NtClose( keyHandle ); } } #endif
#if MULTIPROCESSOR
VOID StartQueueDepthComputations( PWORK_QUEUE queue ) { LARGE_INTEGER currentTime;
PAGED_CODE();
if( SrvNumberOfProcessors == 1 ) return;
//
// We're going to schedule a dpc to call the 'ComputeAvgQueueDepth' routine
// Initialize the dpc
//
KeInitializeDpc( &queue->QueueAvgDpc, ComputeAvgQueueDepth, queue );
//
// We want to make sure the dpc runs on the same processor handling the
// queue -- to avoid thrashing the cache
//
KeSetTargetProcessorDpc( &queue->QueueAvgDpc, (CCHAR)(queue - SrvWorkQueues));
//
// Initialize a timer object to schedule our dpc later
//
KeInitializeTimer( &queue->QueueAvgTimer ); KeQuerySystemTime( ¤tTime ); queue->NextAvgUpdateTime.QuadPart = currentTime.QuadPart + SrvQueueCalc.QuadPart;
//
// Initialize the sample vector
//
queue->NextSample = queue->DepthSamples; RtlZeroMemory( queue->DepthSamples, sizeof( queue->DepthSamples ) );
//
// And start it going!
//
KeSetTimer( &queue->QueueAvgTimer, queue->NextAvgUpdateTime, &queue->QueueAvgDpc ); }
VOID StopQueueDepthComputations( PWORK_QUEUE queue ) { KIRQL oldIrql;
if( SrvNumberOfProcessors == 1 ) return;
KeInitializeEvent( &queue->AvgQueueDepthTerminationEvent, NotificationEvent, FALSE );
ACQUIRE_SPIN_LOCK( &queue->SpinLock, &oldIrql );
queue->NextSample = NULL;
RELEASE_SPIN_LOCK( &queue->SpinLock, oldIrql );
//
// Cancel the computation timer. If this works, then we know that
// the DPC code is not running. Otherwise, it is running or queued
// to run and we need to wait until it completes.
//
if( !KeCancelTimer( &queue->QueueAvgTimer ) ) { KeWaitForSingleObject( &queue->AvgQueueDepthTerminationEvent, Executive, KernelMode, FALSE, NULL ); } }
VOID ComputeAvgQueueDepth ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { LARGE_INTEGER currentTime; PWORK_QUEUE queue = (PWORK_QUEUE)DeferredContext;
ACQUIRE_DPC_SPIN_LOCK( &queue->SpinLock );
if( queue->NextSample == NULL ) {
KeSetEvent( &queue->AvgQueueDepthTerminationEvent, 0, FALSE );
} else {
//
// Compute the sliding window average by taking a queue depth
// sample, removing the old sample value from the running sum
// and adding in the new value
//
currentTime.LowPart= PtrToUlong(SystemArgument1); currentTime.HighPart = PtrToUlong(SystemArgument2);
queue->AvgQueueDepthSum -= *queue->NextSample; *(queue->NextSample) = KeReadStateQueue( &queue->Queue ); queue->AvgQueueDepthSum += *queue->NextSample;
if( ++(queue->NextSample) == &queue->DepthSamples[ QUEUE_SAMPLES ] ) queue->NextSample = queue->DepthSamples;
queue->NextAvgUpdateTime.QuadPart = currentTime.QuadPart + SrvQueueCalc.QuadPart;
KeSetTimer( &queue->QueueAvgTimer, queue->NextAvgUpdateTime, &queue->QueueAvgDpc ); }
RELEASE_DPC_SPIN_LOCK( &queue->SpinLock ); } #endif // MULTIPROCESSOR
|