NT4/private/ntos/srv/fsddisp.c
2020-09-30 17:12:29 +02:00

1566 lines
44 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
fsddisp.c
Abstract:
This module implements the File System Driver for the LAN Manager
server.
Author:
David Treadwell (davidtr) 20-May-1990
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_FSDDISP
#define CHANGE_HEURISTIC(heuristic) \
(newValues->HeuristicsChangeMask & SRV_HEUR_ ## heuristic) != 0
//
// Forward declarations
//
STATIC
NTSTATUS
SrvFsdDispatchFsControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);
VOID
QueueConfigurationIrp (
IN PIRP Irp
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvFsdDispatch )
#pragma alloc_text( PAGE, SrvFsdDispatchFsControl )
#pragma alloc_text( PAGE, QueueConfigurationIrp )
#endif
NTSTATUS
SrvFsdDispatch (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the dispatch routine for the LAN Manager server FSD. At the
present time, the server FSD does not accept any I/O requests.
Arguments:
DeviceObject - Pointer to device object for target device
Irp - Pointer to I/O request packet
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irpSp;
PAGED_CODE( );
DeviceObject; // prevent compiler warnings
irpSp = IoGetCurrentIrpStackLocation( Irp );
switch ( irpSp->MajorFunction ) {
case IRP_MJ_CREATE:
ACQUIRE_LOCK( &SrvConfigurationLock );
if( SrvOpenCount == 0 ) {
//
// This is the first open. Let's not allow it if the server
// seems to be in a weird state.
//
if( SrvFspActive != FALSE || SrvFspTransitioning != FALSE ) {
//
// How can this be? Better not let anybody in, since we're sick
//
RELEASE_LOCK( &SrvConfigurationLock );
status = STATUS_ACCESS_DENIED;
break;
}
} else if( SrvFspActive && SrvFspTransitioning ) {
//
// We currently have some open handles, but
// we are in the middle of terminating. Don't let new
// opens in
//
RELEASE_LOCK( &SrvConfigurationLock );
status = STATUS_ACCESS_DENIED;
break;
}
SrvOpenCount++;
RELEASE_LOCK( &SrvConfigurationLock );
break;
case IRP_MJ_CLEANUP:
//
// Stop SmbTrace if the one closing is the client who started it.
//
SmbTraceStop( irpSp->FileObject, SMBTRACE_SERVER );
break;
case IRP_MJ_CLOSE:
ACQUIRE_LOCK( &SrvConfigurationLock );
if( --SrvOpenCount == 0 ) {
if( SrvFspActive && !SrvFspTransitioning ) {
//
// Uh oh. This is our last close, and we think
// we're still running. We can't run sensibly
// without srvsvc to help out. Suicide time!
//
SrvXsActive = FALSE;
SrvFspTransitioning = TRUE;
IoMarkIrpPending( Irp );
QueueConfigurationIrp( Irp );
RELEASE_LOCK( &SrvConfigurationLock );
status = STATUS_PENDING;
goto exit;
}
}
RELEASE_LOCK( &SrvConfigurationLock );
break;
case IRP_MJ_FILE_SYSTEM_CONTROL:
status = SrvFsdDispatchFsControl( DeviceObject, Irp, irpSp );
goto exit;
case IRP_MJ_SHUTDOWN:
//
// Acquire the configuration lock.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
//
// If the server is not running, or if it is in the process of
// shutting down, ignore this request.
//
if ( SrvFspActive && !SrvFspTransitioning ) {
SrvFspTransitioning = TRUE;
//
// Queue the request to the FSP for processing.
//
// *** Note that the request must be queued while the
// configuration lock is held in order to prevent an
// add/delete/etc request from checking the server state
// before a shutdown request, but being queued after
// that request.
//
IoMarkIrpPending( Irp );
QueueConfigurationIrp( Irp );
RELEASE_LOCK( &SrvConfigurationLock );
status = STATUS_PENDING;
goto exit;
}
RELEASE_LOCK( &SrvConfigurationLock );
break;
default:
IF_DEBUG(ERRORS) {
SrvPrint1(
"SrvFsdDispatch: Invalid major function %lx\n",
irpSp->MajorFunction
);
}
status = STATUS_NOT_IMPLEMENTED;
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, 2 );
exit:
return status;
} // SrvFsdDispatch
NTSTATUS
SrvFsdDispatchFsControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
This routine handles device IO control requests to the server,
including starting the server, stopping the server, and more.
Arguments:
DeviceObject - Pointer to device object for target device
Irp - Pointer to I/O request packet
IrpSp - Pointer to the current IRP stack location
Return Value:
NTSTATUS -- Indicates whether the request was successfully handled.
--*/
{
NTSTATUS status;
ULONG code;
DeviceObject; // prevent compiler warnings
//
// Initialize the I/O status block.
//
Irp->IoStatus.Status = STATUS_PENDING;
Irp->IoStatus.Information = 0;
//
// Acquire the configuration lock.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
//
// Process the request if possible.
//
code = IrpSp->Parameters.FileSystemControl.FsControlCode;
switch ( code ) {
case FSCTL_SRV_STARTUP: {
PSERVER_REQUEST_PACKET srp;
ULONG srpLength;
PVOID inputBuffer;
ULONG inputBufferLength;
//
// Get a pointer to the SRP that describes the set info request
// for the startup server configuration, and the buffer that
// contains this information.
//
srp = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
srpLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
inputBuffer = Irp->UserBuffer;
inputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
//
// If the server FSP is already started, or is in the process of
// starting up, reject this request.
//
if ( SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP already started.\n" );
//}
srp->ErrorCode = NERR_ServiceInstalled;
status = STATUS_SUCCESS;
goto exit_with_lock;
}
//
// Make sure that the buffer was large enough to be an SRP.
//
if ( srpLength < sizeof(SERVER_REQUEST_PACKET) ) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_lock;
}
//
// If a domain name was specified in the SRP, the buffer field
// contains an offset rather than a pointer. Convert the offset
// to a pointer and verify that that it is a legal pointer.
//
OFFSET_TO_POINTER( srp->Name1.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name1.Buffer, srp, srpLength ) ) {
status = STATUS_ACCESS_VIOLATION;
goto exit_with_lock;
}
//
// If a server name was specified in the SRP, the buffer field
// contains an offset rather than a pointer. Convert the offset
// to a pointer and verify that that it is a legal pointer.
//
OFFSET_TO_POINTER( srp->Name2.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name2.Buffer, srp, srpLength ) ) {
status = STATUS_ACCESS_VIOLATION;
goto exit_with_lock;
}
//
// Call SrvNetServerSetInfo to set the initial server configuration
// information.
//
status = SrvNetServerSetInfo(
srp,
inputBuffer,
inputBufferLength
);
//
// Indicate that the server is starting up. This prevents
// further startup requests from being issued.
//
SrvFspTransitioning = TRUE;
break;
}
case FSCTL_SRV_SHUTDOWN: {
//
// If the server is not running, or if it is in the process
// of shutting down, ignore this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//
// 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 ( SrvOpenCount != 1 ) {
status = STATUS_SERVER_HAS_OPEN_HANDLES;
} else {
status = STATUS_SUCCESS;
}
goto exit_with_lock;
}
//
// Indicate that the server is shutting down. This prevents
// further requests from being issued until the server is
// restarted.
//
SrvFspTransitioning = TRUE;
//
// If SmbTrace is running, stop it.
//
SmbTraceStop( NULL, SMBTRACE_SERVER );
break;
}
case FSCTL_SRV_REGISTRY_CHANGE:
case FSCTL_SRV_BEGIN_PNP_NOTIFICATIONS:
case FSCTL_SRV_XACTSRV_CONNECT:
{
if( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
break;
}
case FSCTL_SRV_XACTSRV_DISCONNECT: {
//
// If the server is not running, or if it is in the process
// of shutting down, ignore this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
status = STATUS_SUCCESS;
goto exit_with_lock;
}
break;
}
case FSCTL_SRV_IPX_SMART_CARD_START: {
//
// If the server is not running, or if it is in the process of
// shutting down, ignore this request.
//
if( !SrvFspActive || SrvFspTransitioning ) {
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
//
// Make sure the caller is a driver
//
if( Irp->RequestorMode != KernelMode ) {
status = STATUS_ACCESS_DENIED;
goto exit_with_lock;
}
//
// Make sure the buffer is big enough
//
if( IrpSp->Parameters.FileSystemControl.InputBufferLength <
sizeof( SrvIpxSmartCard ) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto exit_with_lock;
}
if( SrvIpxSmartCard.Open == NULL ) {
PSRV_IPX_SMART_CARD pSipx;
//
// Load up the pointers
//
pSipx = (PSRV_IPX_SMART_CARD)(Irp->AssociatedIrp.SystemBuffer);
if( pSipx == NULL ) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_lock;
}
if( pSipx->Read && pSipx->Close && pSipx->DeRegister && pSipx->Open ) {
IF_DEBUG( SIPX ) {
KdPrint(( "Accepting entry points for IPX Smart Card:\n" ));
KdPrint(( " Open %X, Read %X, Close %x, DeRegister %x",
SrvIpxSmartCard.Open,
SrvIpxSmartCard.Read,
SrvIpxSmartCard.Close,
SrvIpxSmartCard.DeRegister
));
}
//
// First set our entry point
//
pSipx->ReadComplete = SrvIpxSmartCardReadComplete;
//
// Now accept the card's entry points.
//
SrvIpxSmartCard.Read = pSipx->Read;
SrvIpxSmartCard.Close= pSipx->Close;
SrvIpxSmartCard.DeRegister = pSipx->DeRegister;
SrvIpxSmartCard.Open = pSipx->Open;
status = STATUS_SUCCESS;
} else {
status = STATUS_INVALID_PARAMETER;
}
} else {
status = STATUS_DEVICE_ALREADY_ATTACHED;
}
goto exit_with_lock;
break;
}
case FSCTL_SRV_SEND_DATAGRAM:
{
PVOID systemBuffer;
ULONG systemBufferLength;
PVOID buffer1;
ULONG buffer1Length;
PVOID buffer2;
ULONG buffer2Length;
PSERVER_REQUEST_PACKET srp;
//
// Ignore this request if the server is not active.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
status = STATUS_SUCCESS;
goto exit_with_lock;
}
//
// Determine the input buffer lengths, and make sure that the
// first buffer is large enough to be an SRP.
//
buffer1Length = IrpSp->Parameters.FileSystemControl.InputBufferLength;
buffer2Length = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
//
// Make the first buffer size an even multiple of four so that
// the second buffer starts on a longword boundary.
//
buffer1Length = (buffer1Length + 3) & ~3;
//
// Allocate a single buffer that will hold both input buffers.
//
systemBufferLength = buffer1Length + buffer2Length;
systemBuffer = ExAllocatePool( PagedPool, systemBufferLength );
if ( systemBuffer == NULL ) {
status = STATUS_INSUFF_SERVER_RESOURCES;
goto exit_with_lock;
}
buffer1 = systemBuffer;
buffer2 = (PCHAR)systemBuffer + buffer1Length;
//
// Copy the information into the buffers.
//
RtlCopyMemory(
buffer1,
IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
IrpSp->Parameters.FileSystemControl.InputBufferLength
);
if ( buffer2Length > 0 ) {
RtlCopyMemory( buffer2, Irp->UserBuffer, buffer2Length );
}
//
// If a name was specified in the SRP, the buffer field will
// contain an offset rather than a pointer. Convert the offset
// to a pointer and verify that that it is a legal pointer.
//
srp = buffer1;
OFFSET_TO_POINTER( srp->Name1.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name1.Buffer, srp, buffer1Length ) ) {
status = STATUS_ACCESS_VIOLATION;
ExFreePool( buffer1 );
goto exit_with_lock;
}
OFFSET_TO_POINTER( srp->Name2.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name2.Buffer, srp, buffer1Length ) ) {
status = STATUS_ACCESS_VIOLATION;
ExFreePool( buffer1 );
goto exit_with_lock;
}
Irp->AssociatedIrp.SystemBuffer = systemBuffer;
break;
}
case FSCTL_SRV_SHARE_STATE_CHANGE:
{
ULONG srpLength;
PSERVER_REQUEST_PACKET srp;
PSHARE share;
if ( !SrvFspActive || SrvFspTransitioning ) {
status = STATUS_SUCCESS;
goto exit_with_lock;
}
srp = Irp->AssociatedIrp.SystemBuffer;
srpLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
OFFSET_TO_POINTER( srp->Name1.Buffer, srp );
if (!POINTER_IS_VALID( srp->Name1.Buffer, srp, srpLength)) {
status = STATUS_ACCESS_VIOLATION;
goto exit_with_lock;
}
ACQUIRE_LOCK( &SrvShareLock );
share = SrvFindShare( &srp->Name1 );
if ( share != NULL) {
share->IsDfs = ((srp->Flags & SRP_SET_SHARE_IN_DFS) != 0);
status = STATUS_SUCCESS;
} else {
status = STATUS_OBJECT_NAME_NOT_FOUND;
}
RELEASE_LOCK( &SrvShareLock );
goto exit_with_lock;
break;
}
case FSCTL_SRV_GET_QUEUE_STATISTICS:
{
PSRV_QUEUE_STATISTICS qstats;
SRV_QUEUE_STATISTICS tmpqstats;
PWORK_QUEUE queue;
LONG timeIncrement = (LONG)KeQueryTimeIncrement();
//
// Make sure the server is active.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
if ( IrpSp->Parameters.FileSystemControl.OutputBufferLength <
(SrvNumberOfProcessors+1) * sizeof( *qstats ) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto exit_with_lock;
}
qstats = Irp->AssociatedIrp.SystemBuffer;
//
// Get the data for the normal processor queues
//
for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++, qstats++ ) {
tmpqstats.QueueLength = KeReadStateQueue( &queue->Queue );
tmpqstats.ActiveThreads = queue->Threads - queue->AvailableThreads;
tmpqstats.AvailableThreads = queue->Threads;
tmpqstats.FreeWorkItems = queue->FreeWorkItems; // no lock!
tmpqstats.StolenWorkItems = queue->StolenWorkItems; // no lock!
tmpqstats.NeedWorkItem = queue->NeedWorkItem;
tmpqstats.CurrentClients = queue->CurrentClients;
tmpqstats.BytesReceived.QuadPart = queue->stats.BytesReceived;
tmpqstats.BytesSent.QuadPart = queue->stats.BytesSent;
tmpqstats.ReadOperations.QuadPart = queue->stats.ReadOperations;
tmpqstats.BytesRead.QuadPart = queue->stats.BytesRead;
tmpqstats.WriteOperations.QuadPart = queue->stats.WriteOperations;
tmpqstats.BytesWritten.QuadPart = queue->stats.BytesWritten;
tmpqstats.TotalWorkContextBlocksQueued = queue->stats.WorkItemsQueued;
tmpqstats.TotalWorkContextBlocksQueued.Count *= STATISTICS_SMB_INTERVAL;
tmpqstats.TotalWorkContextBlocksQueued.Time.QuadPart *= timeIncrement;
RtlCopyMemory( qstats, &tmpqstats, sizeof(tmpqstats) );
}
//
// Get the data for the blocking work queue
//
tmpqstats.QueueLength = KeReadStateQueue( &SrvBlockingWorkQueue.Queue );
tmpqstats.ActiveThreads = SrvBlockingWorkQueue.Threads -
SrvBlockingWorkQueue.AvailableThreads;
tmpqstats.AvailableThreads = SrvBlockingWorkQueue.Threads;
tmpqstats.FreeWorkItems = SrvBlockingWorkQueue.FreeWorkItems; // no lock!
tmpqstats.StolenWorkItems = SrvBlockingWorkQueue.StolenWorkItems; // no lock!
tmpqstats.NeedWorkItem = SrvBlockingWorkQueue.NeedWorkItem;
tmpqstats.CurrentClients = SrvBlockingWorkQueue.CurrentClients;
tmpqstats.BytesReceived.QuadPart = SrvBlockingWorkQueue.stats.BytesReceived;
tmpqstats.BytesSent.QuadPart = SrvBlockingWorkQueue.stats.BytesSent;
tmpqstats.ReadOperations.QuadPart = SrvBlockingWorkQueue.stats.ReadOperations;
tmpqstats.BytesRead.QuadPart = SrvBlockingWorkQueue.stats.BytesRead;
tmpqstats.WriteOperations.QuadPart = SrvBlockingWorkQueue.stats.WriteOperations;
tmpqstats.BytesWritten.QuadPart = SrvBlockingWorkQueue.stats.BytesWritten;
tmpqstats.TotalWorkContextBlocksQueued
= SrvBlockingWorkQueue.stats.WorkItemsQueued;
tmpqstats.TotalWorkContextBlocksQueued.Count *= STATISTICS_SMB_INTERVAL;
tmpqstats.TotalWorkContextBlocksQueued.Time.QuadPart *= timeIncrement;
RtlCopyMemory( qstats, &tmpqstats, sizeof(tmpqstats) );
Irp->IoStatus.Information = (SrvNumberOfProcessors + 1) * sizeof( *qstats );
status = STATUS_SUCCESS;
goto exit_with_lock;
break;
}
case FSCTL_SRV_GET_STATISTICS:
//
// Make sure that the server is active.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
{
SRV_STATISTICS tmpStatistics;
//
// Make sure that the user buffer is large enough to hold the
// statistics database.
//
if ( IrpSp->Parameters.FileSystemControl.OutputBufferLength <
sizeof(SRV_STATISTICS) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto exit_with_lock;
}
//
// Copy the statistics database to the user buffer. Store
// the statistics in a temporary buffer so we can convert
// the tick count stored to system time.
//
SrvUpdateStatisticsFromQueues( &tmpStatistics );
tmpStatistics.TotalWorkContextBlocksQueued.Time.QuadPart *=
(LONG)KeQueryTimeIncrement();
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&tmpStatistics,
sizeof(tmpStatistics)
);
Irp->IoStatus.Information = sizeof(tmpStatistics);
}
status = STATUS_SUCCESS;
goto exit_with_lock;
#if SRVDBG_STATS || SRVDBG_STATS2
case FSCTL_SRV_GET_DEBUG_STATISTICS:
//
// Make sure that the server is active.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
{
PSRV_STATISTICS_DEBUG stats;
//
// Make sure that the user buffer is large enough to hold the
// statistics database.
//
if ( IrpSp->Parameters.FileSystemControl.OutputBufferLength <
FIELD_OFFSET(SRV_STATISTICS_DEBUG,QueueStatistics) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto exit_with_lock;
}
//
// Acquire the statistics lock, then copy the statistics database
// to the user buffer.
//
stats = (PSRV_STATISTICS_DEBUG)Irp->AssociatedIrp.SystemBuffer;
RtlCopyMemory(
stats,
&SrvDbgStatistics,
FIELD_OFFSET(SRV_STATISTICS_DEBUG,QueueStatistics) );
Irp->IoStatus.Information =
FIELD_OFFSET(SRV_STATISTICS_DEBUG,QueueStatistics);
if ( IrpSp->Parameters.FileSystemControl.OutputBufferLength >=
sizeof(SrvDbgStatistics) ) {
PWORK_QUEUE queue;
ULONG i, j;
i = 0;
stats->QueueStatistics[i].Depth = 0;
stats->QueueStatistics[i].Threads = 0;
#if SRVDBG_STATS2
stats->QueueStatistics[i].ItemsQueued = 0;
stats->QueueStatistics[i].MaximumDepth = 0;
#endif
for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) {
stats->QueueStatistics[i].Depth += KeReadStateQueue( &queue->Queue );
stats->QueueStatistics[i].Threads += queue->Threads;
#if SRVDBG_STATS2
stats->QueueStatistics[i].ItemsQueued += queue->ItemsQueued;
stats->QueueStatistics[i].MaximumDepth += queue->MaximumDepth + 1;
#endif
}
Irp->IoStatus.Information = sizeof(SrvDbgStatistics);
}
}
status = STATUS_SUCCESS;
goto exit_with_lock;
#endif // SRVDBG_STATS || SRVDBG_STATS2
//
// The follwing APIs must be processed in the server FSP because
// they open or close handles.
//
case FSCTL_SRV_NET_CHARDEV_CONTROL:
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 buffer1;
PVOID buffer2;
PVOID systemBuffer;
ULONG buffer1Length;
ULONG buffer2Length;
ULONG systemBufferLength;
//
// Get the server request packet pointer.
//
srp = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
//
// If the server is not running, or if it is in the process
// of shutting down, reject this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
srp->ErrorCode = NERR_ServerNotStarted;
status = STATUS_SUCCESS;
goto exit_with_lock;
}
//
// Determine the input buffer lengths, and make sure that the
// first buffer is large enough to be an SRP.
//
buffer1Length = IrpSp->Parameters.FileSystemControl.InputBufferLength;
buffer2Length = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
if ( buffer1Length < sizeof(SERVER_REQUEST_PACKET) ) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_lock;
}
//
// Make the first buffer size an even multiple of four so that
// the second buffer starts on a longword boundary.
//
buffer1Length = (buffer1Length + 3) & ~3;
//
// Allocate a single buffer that will hold both input buffers.
// Note that the SRP part of the first buffer is copied back
// to the user as an output buffer.
//
systemBufferLength = buffer1Length + buffer2Length;
systemBuffer = ExAllocatePool( PagedPool, systemBufferLength );
if ( systemBuffer == NULL ) {
status = STATUS_INSUFF_SERVER_RESOURCES;
goto exit_with_lock;
}
buffer1 = systemBuffer;
buffer2 = (PCHAR)systemBuffer + buffer1Length;
//
// Copy the information into the buffers.
//
RtlCopyMemory(
buffer1,
srp,
IrpSp->Parameters.FileSystemControl.InputBufferLength
);
if ( buffer2Length > 0 ) {
RtlCopyMemory( buffer2, Irp->UserBuffer, buffer2Length );
}
//
// If a name was specified in the SRP, the buffer field will
// contain an offset rather than a pointer. Convert the offset
// to a pointer and verify that that it is a legal pointer.
//
srp = buffer1;
OFFSET_TO_POINTER( srp->Name1.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name1.Buffer, srp, buffer1Length ) ) {
status = STATUS_ACCESS_VIOLATION;
ExFreePool( buffer1 );
goto exit_with_lock;
}
OFFSET_TO_POINTER( srp->Name2.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name2.Buffer, srp, buffer1Length ) ) {
status = STATUS_ACCESS_VIOLATION;
ExFreePool( buffer1 );
goto exit_with_lock;
}
//
// Set up pointers in the IRP. The system buffer points to the
// buffer we just allocated to contain the input buffers. User
// buffer points to the SRP from the server service. This
// allows the SRP to be used as an output buffer-- the number of
// bytes specified by the Information field of the IO status
// block are copied from the system buffer to the user buffer at
// IO completion.
//
Irp->AssociatedIrp.SystemBuffer = systemBuffer;
Irp->UserBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
//
// Set up other fields in the IRP so that the SRP is copied from
// the system buffer to the user buffer, and the system buffer
// is deallocated by IO completion.
//
Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER |
IRP_INPUT_OPERATION;
Irp->IoStatus.Information = sizeof(SERVER_REQUEST_PACKET);
break;
}
//
// The following APIs should be processed in the server FSP because
// they reference and dereference structures, which could lead to
// handles being closed. However, it was too hard to change this
// (because of the need to return a separate SRP and data buffer) at
// the time this was realized (just before Product 1 shipment), so
// they are processed in the FSD, and all calls to NtClose attach to
// the server process first if necessary.
//
case FSCTL_SRV_NET_CHARDEV_ENUM:
case FSCTL_SRV_NET_CHARDEVQ_ENUM:
case FSCTL_SRV_NET_CONNECTION_ENUM:
case FSCTL_SRV_NET_FILE_ENUM:
case FSCTL_SRV_NET_SERVER_DISK_ENUM:
case FSCTL_SRV_NET_SERVER_XPORT_ENUM:
case FSCTL_SRV_NET_SESSION_ENUM:
case FSCTL_SRV_NET_SHARE_ENUM:
//
// These APIs are processed here in the server FSD.
//
case FSCTL_SRV_NET_CHARDEVQ_PURGE:
case FSCTL_SRV_NET_CHARDEVQ_SET_INFO:
case FSCTL_SRV_NET_SERVER_SET_INFO:
case FSCTL_SRV_NET_SHARE_SET_INFO:
case FSCTL_SRV_NET_STATISTICS_GET:
{
PSERVER_REQUEST_PACKET srp;
ULONG buffer1Length;
//
// Get the server request packet pointer.
//
srp = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
buffer1Length = IrpSp->Parameters.FileSystemControl.InputBufferLength;
//
// If the server is not running, or if it is in the process
// of shutting down, reject this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
srp->ErrorCode = NERR_ServerNotStarted;
status = STATUS_SUCCESS;
goto exit_with_lock;
}
//
// Increment the count of API requests in the server FSD.
//
SrvApiRequestCount++;
//
// Make sure that the buffer was large enough to be an SRP.
//
if ( buffer1Length < sizeof(SERVER_REQUEST_PACKET) ) {
status = STATUS_INVALID_PARAMETER;
goto exit_with_lock;
}
//
// If a name was specified in the SRP, the buffer field will
// contain an offset rather than a pointer. Convert the offset
// to a pointer and verify that that it is a legal pointer.
//
OFFSET_TO_POINTER( srp->Name1.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name1.Buffer, srp, buffer1Length ) ) {
status = STATUS_ACCESS_VIOLATION;
goto exit_with_lock;
}
OFFSET_TO_POINTER( srp->Name2.Buffer, srp );
if ( !POINTER_IS_VALID( srp->Name2.Buffer, srp, buffer1Length ) ) {
status = STATUS_ACCESS_VIOLATION;
goto exit_with_lock;
}
//
// We don't need the configuration lock any more.
//
RELEASE_LOCK( &SrvConfigurationLock );
//
// Dispatch the API request to the appripriate API processing
// routine. All these API requests are handled in the FSD.
//
status = SrvApiDispatchTable[ SRV_API_INDEX(code) ](
srp,
Irp->UserBuffer,
IrpSp->Parameters.FileSystemControl.OutputBufferLength
);
//
// Decrement the count of outstanding API requests in the
// server. Hold the configuration lock while doing this, as it
// protects the API count variable.
//
ACQUIRE_LOCK( &SrvConfigurationLock );
SrvApiRequestCount--;
//
// Check to see whether the server is transitioning from started
// to not started. If so, and if this is the last API request
// to be completed, then set the API completion event which the
// shutdown code is waiting on.
//
// Since we checked SrvFspTransitioning at the start of the
// request, we know that the shutdown came after we started
// processing the API. If SrvApiRequestCount is 0, then there
// are no other threads in the FSD processing API requests.
// Therefore, it is safe for the shutdown code to proceed with
// the knowledge that no other thread in the server is
// operating.
//
if ( SrvFspTransitioning && SrvApiRequestCount == 0 ) {
KeSetEvent( &SrvApiCompletionEvent, 0, FALSE );
}
goto exit_with_lock;
}
#ifdef OLDNET
case FSCTL_SRV_SET_DEBUG:
{
ULONG inputLength;
ULONG outputLength;
FSCTL_SRV_SET_DEBUG_IN_OUT oldValues;
extern ULONG CcDebugTraceLevel;
extern ULONG PbDebugTraceLevel;
extern ULONG FatDebugTraceLevel;
//
// Make sure that the input buffer is the right size.
//
inputLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
if ( (inputLength != 0) &&
(inputLength < sizeof(FSCTL_SRV_SET_DEBUG_IN_OUT)) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto exit_with_lock;
}
outputLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
if ( outputLength != 0 ) {
//
// Make sure that the output buffer is the right size.
//
if ( outputLength < sizeof(FSCTL_SRV_SET_DEBUG_IN_OUT) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto exit_with_lock;
}
//
// Save the old flags and heuristics in a local structure.
// (The input and output buffers coincide, so we can't copy
// the old values into the output buffer yet.)
//
oldValues.SrvDebugOff = 0xffffffff;
oldValues.SmbDebugOff = 0xffffffff;
#if SRVDBG
oldValues.SrvDebugOn = SrvDebug.QuadPart;
oldValues.SmbDebugOn = SmbDebug;
#else
oldValues.SrvDebugOn = 0;
oldValues.SmbDebugOn = 0;
#endif
oldValues.HeuristicsChangeMask = 0xffffffff;
oldValues.MaxCopyReadLength = SrvMaxCopyReadLength;
oldValues.MaxCopyWriteLength = SrvMaxCopyWriteLength;
oldValues.EnableOplocks = SrvEnableOplocks;
oldValues.EnableFcbOpens = SrvEnableFcbOpens;
oldValues.EnableSoftCompatibility = SrvEnableSoftCompatibility;
oldValues.EnableRawMode = SrvEnableRawMode;
oldValues.CcDebugOff = 0xffffffff;
#ifdef CCDBG
oldValues.CcDebugOn = CcDebugTraceLevel;
#else
oldValues.CcDebugOn = 0;
#endif
oldValues.PbDebugOff = 0xffffffff;
#ifdef PBDBG
oldValues.PbDebugOn = PbDebugTraceLevel;
#else
oldValues.PbDebugOn = 0;
#endif
oldValues.FatDebugOff = 0xffffffff;
#ifdef FATDBG
oldValues.FatDebugOn = FatDebugTraceLevel;
#else
oldValues.FatDebugOn = 0;
#endif
}
//
// If requested, change flags and heuristics as specified.
//
if ( inputLength != 0 ) {
PFSCTL_SRV_SET_DEBUG_IN_OUT newValues =
Irp->AssociatedIrp.SystemBuffer;
#if SRVDBG
SrvDebug &= ~newValues->SrvDebugOff;
SrvDebug |= newValues->SrvDebugOn;
SmbDebug &= ~newValues->SmbDebugOff;
SmbDebug |= newValues->SmbDebugOn;
#endif
if ( CHANGE_HEURISTIC(MAX_COPY_READ) ) {
SrvMaxCopyReadLength = newValues->MaxCopyReadLength;
}
if ( CHANGE_HEURISTIC(MAX_COPY_WRITE) ) {
SrvMaxCopyWriteLength = newValues->MaxCopyWriteLength;
}
if ( CHANGE_HEURISTIC(OPLOCKS) ) {
SrvEnableOplocks = newValues->EnableOplocks;
}
if ( CHANGE_HEURISTIC(FCB_OPENS) ) {
SrvEnableFcbOpens = newValues->EnableFcbOpens;
}
if ( CHANGE_HEURISTIC(SOFT_COMPATIBILITY) ) {
SrvEnableSoftCompatibility = newValues->EnableSoftCompatibility;
}
if ( CHANGE_HEURISTIC(RAW_MODE) ) {
SrvEnableRawMode = newValues->EnableRawMode;
}
#ifdef CCDBG
CcDebugTraceLevel &= ~newValues->CcDebugOff;
CcDebugTraceLevel |= newValues->CcDebugOn;
#endif
#ifdef PBDBG
PbDebugTraceLevel &= ~newValues->PbDebugOff;
PbDebugTraceLevel |= newValues->PbDebugOn;
#endif
#ifdef FATDBG
FatDebugTraceLevel &= ~newValues->FatDebugOff;
FatDebugTraceLevel |= newValues->FatDebugOn;
#endif
}
//
// If requested, copy the old values into the output buffer.
//
if ( outputLength != 0 ) {
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&oldValues,
sizeof(FSCTL_SRV_SET_DEBUG_IN_OUT)
);
Irp->IoStatus.Information = sizeof(FSCTL_SRV_SET_DEBUG_IN_OUT);
}
status = STATUS_SUCCESS;
goto exit_with_lock;
}
#endif // def OLDNET
case FSCTL_SRV_START_SMBTRACE:
if ( SmbTraceActive[SMBTRACE_SERVER] ) {
status = STATUS_SHARING_VIOLATION;
goto exit_with_lock;
}
if ( !SrvFspActive || SrvFspTransitioning ) {
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
break; // FSP continues the processing.
case FSCTL_SRV_END_SMBTRACE:
//
// If the server is not running, or if it is in the process
// of shutting down, reject this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
//
// Attempt to stop SmbTrace. It will likely return
// STATUS_PENDING, indicating that it is in the process
// of shutting down. STATUS_PENDING is a poor value
// to return (according to an assertion in io\iosubs.c)
// so we convert it to success. Better would be for
// SmbTraceStop to wait until it has successfully stopped.
//
status = SmbTraceStop( NULL, SMBTRACE_SERVER );
//
// Complete the request with success.
//
status = STATUS_SUCCESS;
goto exit_with_lock;
case FSCTL_SRV_PAUSE:
//
// If the server is not running, or if it is in the process
// of shutting down, reject this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
SrvPaused = TRUE;
status = STATUS_SUCCESS;
goto exit_with_lock;
case FSCTL_SRV_CONTINUE:
//
// If the server is not running, or if it is in the process
// of shutting down, reject this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
SrvPaused = FALSE;
status = STATUS_SUCCESS;
goto exit_with_lock;
case FSCTL_SRV_GET_CHALLENGE:
{
PLIST_ENTRY sessionEntry;
PLUID inputLuid;
PSESSION session;
//
// If the server is not running, or if it is in the process
// of shutting down, reject this request.
//
if ( !SrvFspActive || SrvFspTransitioning ) {
//IF_DEBUG(ERRORS) {
// SrvPrint0( "LAN Manager server FSP not started.\n" );
//}
status = STATUS_SERVER_NOT_STARTED;
goto exit_with_lock;
}
if ( IrpSp->Parameters.FileSystemControl.InputBufferLength <
sizeof(LUID) ||
IrpSp->Parameters.FileSystemControl.OutputBufferLength <
sizeof(session->NtUserSessionKey) ) {
status = STATUS_BUFFER_TOO_SMALL;
goto exit_with_lock;
}
RELEASE_LOCK( &SrvConfigurationLock );
inputLuid = (PLUID)Irp->AssociatedIrp.SystemBuffer;
//
// Acquire the lock that protects the session list and walk the
// list looking for a user token that matches the one specified
// in the input buffer.
//
ACQUIRE_LOCK( SrvSessionList.Lock );
for ( sessionEntry = SrvSessionList.ListHead.Flink;
sessionEntry != &SrvSessionList.ListHead;
sessionEntry = sessionEntry->Flink ) {
session = CONTAINING_RECORD(
sessionEntry,
SESSION,
GlobalSessionListEntry
);
if ( RtlEqualLuid( inputLuid, &session->LogonId ) ) {
//
// We found a match. Write the NT user session key into
// the output buffer.
//
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
session->NtUserSessionKey,
sizeof(session->NtUserSessionKey)
);
RELEASE_LOCK( SrvSessionList.Lock );
Irp->IoStatus.Information = sizeof(session->NtUserSessionKey);
status = STATUS_SUCCESS;
goto exit_without_lock;
}
}
RELEASE_LOCK( SrvSessionList.Lock );
//
// There was no matching token in our session list. Fail the
// request.
//
status = STATUS_NO_TOKEN;
goto exit_without_lock;
}
default:
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"SrvFsdDispatchFsControl: Invalid I/O control "
"code received: %lx\n",
IrpSp->Parameters.FileSystemControl.FsControlCode,
NULL
);
status = STATUS_INVALID_PARAMETER;
goto exit_with_lock;
}
//
// Queue the request to the FSP for processing.
//
// *** Note that the request must be queued while the configuration
// lock is held in order to prevent an add/delete/etc request
// from checking the server state before a shutdown request, but
// being queued after that request.
//
IoMarkIrpPending( Irp );
QueueConfigurationIrp( Irp );
RELEASE_LOCK( &SrvConfigurationLock );
return STATUS_PENDING;
exit_with_lock:
RELEASE_LOCK( &SrvConfigurationLock );
exit_without_lock:
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, 2 );
return status;
} // SrvFsdDispatchFsControl
VOID
QueueConfigurationIrp (
IN PIRP Irp
)
{
PWORK_QUEUE_ITEM p;
PAGED_CODE( );
InterlockedIncrement( (PLONG)&SrvConfigurationIrpsInProgress );
SrvInsertTailList(
&SrvConfigurationWorkQueue,
&Irp->Tail.Overlay.ListEntry
);
//
// Hunt for a SrvConfigurationThreadWorkItem we can use. If they
// are all occupied, this means that config threads are going to
// run and we don't need to worry about it.
//
for( p = SrvConfigurationThreadWorkItem;
p < &SrvConfigurationThreadWorkItem[ MAX_CONFIG_WORK_ITEMS ];
p++ ) {
if( p->Parameter == 0 ) {
p->Parameter = p;
ExQueueWorkItem( p, DelayedWorkQueue );
break;
}
}
} // QueueConfigurationIrp