|
|
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
class.c
Abstract:
SCSI class driver routines
Environment:
kernel mode only
Notes:
Revision History:
--*/
#define CLASS_INIT_GUID 0
#include "classp.h"
#include "debug.h"
ULONG BreakOnClose = 0;
PUCHAR LockTypeStrings[] = { "Simple", "Secure", "Internal" };
PFILE_OBJECT_EXTENSION ClasspGetFsContext( IN PCOMMON_DEVICE_EXTENSION CommonExtension, IN PFILE_OBJECT FileObject );
VOID ClasspCleanupDisableMcn( IN PFILE_OBJECT_EXTENSION FsContext );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ClassCreateClose)
#pragma alloc_text(PAGE, ClasspCreateClose)
#pragma alloc_text(PAGE, ClasspCleanupProtectedLocks)
#pragma alloc_text(PAGE, ClasspEjectionControl)
#pragma alloc_text(PAGE, ClasspCleanupDisableMcn)
#pragma alloc_text(PAGE, ClasspGetFsContext)
#endif
NTSTATUS ClassCreateClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
SCSI class driver create and close routine. This is called by the I/O system when the device is opened or closed.
Arguments:
DriverObject - Pointer to driver object created by system.
Irp - IRP involved.
Return Value:
Device-specific drivers return value or STATUS_SUCCESS.
--*/
{ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; ULONG removeState; NTSTATUS status;
PAGED_CODE();
//
// If we're getting a close request then we know the device object hasn't
// been completely destroyed. Let the driver cleanup if necessary.
//
removeState = ClassAcquireRemoveLock(DeviceObject, Irp);
//
// Invoke the device-specific routine, if one exists. Otherwise complete
// with SUCCESS
//
if((removeState == NO_REMOVE) || IS_CLEANUP_REQUEST(IoGetCurrentIrpStackLocation(Irp)->MajorFunction)) {
status = ClasspCreateClose(DeviceObject, Irp);
if((NT_SUCCESS(status)) && (commonExtension->DevInfo->ClassCreateClose)) {
return commonExtension->DevInfo->ClassCreateClose(DeviceObject, Irp); }
} else { status = STATUS_DEVICE_DOES_NOT_EXIST; }
Irp->IoStatus.Status = status; ClassReleaseRemoveLock(DeviceObject, Irp); ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); return status; }
NTSTATUS ClasspCreateClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
This routine will handle create/close operations for a given classpnp device if the class driver doesn't supply it's own handler. If there is a file object supplied for our driver (if it's a FO_DIRECT_DEVICE_OPEN file object) then it will initialize a file extension on create or destroy the extension on a close.
Arguments:
DeviceObject - the device object being opened or closed.
Irp - the create/close irp
Return Value:
status
--*/ { PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT fileObject = irpStack->FileObject;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
//
// ISSUE-2000/3/28-henrygab - if lower stack fails create/close, we end up
// in an inconsistent state. re-write to verify all args and allocate all
// required resources, then pass the irp down, then complete the
// transaction. this is because we also cannot forward the irp, then fail
// it after it has succeeded a lower-level driver.
//
if(irpStack->MajorFunction == IRP_MJ_CREATE) {
PIO_SECURITY_CONTEXT securityContext = irpStack->Parameters.Create.SecurityContext; DebugPrint((2, "ClasspCREATEClose: create received for device %p\n", DeviceObject)); DebugPrint((2, "ClasspCREATEClose: desired access %lx\n", securityContext->DesiredAccess)); DebugPrint((2, "ClasspCREATEClose: file object %lx\n", irpStack->FileObject));
ASSERT(BreakOnClose == FALSE);
if(irpStack->FileObject != NULL) {
PFILE_OBJECT_EXTENSION fsContext;
//
// Allocate our own file object extension for this device object.
//
status = AllocateDictionaryEntry( &commonExtension->FileObjectDictionary, (ULONGLONG) irpStack->FileObject, sizeof(FILE_OBJECT_EXTENSION), CLASS_TAG_FILE_OBJECT_EXTENSION, &fsContext);
if(NT_SUCCESS(status)) {
RtlZeroMemory(fsContext, sizeof(FILE_OBJECT_EXTENSION));
fsContext->FileObject = irpStack->FileObject; fsContext->DeviceObject = DeviceObject; } else if (status == STATUS_OBJECT_NAME_COLLISION) { status = STATUS_SUCCESS; } }
} else {
DebugPrint((2, "ClasspCreateCLOSE: close received for device %p\n", DeviceObject)); DebugPrint((2, "ClasspCreateCLOSE: file object %p\n", fileObject));
if(irpStack->FileObject != NULL) {
PFILE_OBJECT_EXTENSION fsContext = ClasspGetFsContext(commonExtension, irpStack->FileObject);
DebugPrint((2, "ClasspCreateCLOSE: file extension %p\n", fsContext));
if(fsContext != NULL) {
DebugPrint((2, "ClasspCreateCLOSE: extension is ours - " "freeing\n")); ASSERT(BreakOnClose == FALSE);
ClasspCleanupProtectedLocks(fsContext);
ClasspCleanupDisableMcn(fsContext);
FreeDictionaryEntry(&(commonExtension->FileObjectDictionary), fsContext); } } }
//
// Notify the lower levels about the create or close operation - give them
// a chance to cleanup too.
//
DebugPrint((2, "ClasspCreateClose: %s for devobj %p\n", (NT_SUCCESS(status) ? "Success" : "FAILED"), DeviceObject));
if(NT_SUCCESS(status)) {
KEVENT event;
//
// Set up the event to wait on
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, ClassSignalCompletion, &event, TRUE, TRUE, TRUE);
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
if(status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; }
if (!NT_SUCCESS(status)) { DebugPrint((ClassDebugError, "ClasspCreateClose: Lower driver failed, but we " "succeeded. This is a problem, lock counts will be " "out of sync between levels.\n")); }
}
return status; }
VOID ClasspCleanupProtectedLocks( IN PFILE_OBJECT_EXTENSION FsContext ) { PCOMMON_DEVICE_EXTENSION commonExtension = FsContext->DeviceObject->DeviceExtension;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
ULONG newDeviceLockCount = 1;
PAGED_CODE();
DebugPrint((2, "ClasspCleanupProtectedLocks called for %p\n", FsContext->DeviceObject)); DebugPrint((2, "ClasspCleanupProtectedLocks - FsContext %p is locked " "%d times\n", FsContext, FsContext->LockCount));
ASSERT(BreakOnClose == FALSE);
//
// Synchronize with ejection and ejection control requests.
//
KeEnterCriticalRegion(); KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent), UserRequest, KernelMode, FALSE, NULL);
//
// For each secure lock on this handle decrement the secured lock count
// for the FDO. Keep track of the new value.
//
if(FsContext->LockCount != 0) {
do {
InterlockedDecrement(&FsContext->LockCount);
newDeviceLockCount = InterlockedDecrement(&fdoExtension->ProtectedLockCount);
} while(FsContext->LockCount != 0);
//
// If the new lock count has been dropped to zero then issue a lock
// command to the device.
//
DebugPrint((2, "ClasspCleanupProtectedLocks: FDO secured lock count = %d " "lock count = %d\n", fdoExtension->ProtectedLockCount, fdoExtension->LockCount));
if((newDeviceLockCount == 0) && (fdoExtension->LockCount == 0)) {
SCSI_REQUEST_BLOCK srb = {0}; PCDB cdb; NTSTATUS status;
DebugPrint((2, "ClasspCleanupProtectedLocks: FDO lock count dropped " "to zero\n"));
cdb = (PCDB) &(srb.Cdb);
srb.CdbLength = 6;
cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
//
// TRUE - prevent media removal.
// FALSE - allow media removal.
//
cdb->MEDIA_REMOVAL.Prevent = FALSE;
//
// Set timeout value.
//
srb.TimeOutValue = fdoExtension->TimeOutValue; status = ClassSendSrbSynchronous(fdoExtension->DeviceObject, &srb, NULL, 0, FALSE);
DebugPrint((2, "ClasspCleanupProtectedLocks: unlock request to drive " "returned status %lx\n", status)); } }
KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE); KeLeaveCriticalRegion(); return; }
VOID ClasspCleanupDisableMcn( IN PFILE_OBJECT_EXTENSION FsContext ) { PCOMMON_DEVICE_EXTENSION commonExtension = FsContext->DeviceObject->DeviceExtension;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
ULONG newCount = 1;
PAGED_CODE();
DebugPrint((ClassDebugTrace, "ClasspCleanupDisableMcn called for %p\n", FsContext->DeviceObject)); DebugPrint((ClassDebugTrace, "ClasspCleanupDisableMcn - FsContext %p is disabled " "%d times\n", FsContext, FsContext->McnDisableCount));
//
// For each secure lock on this handle decrement the secured lock count
// for the FDO. Keep track of the new value.
//
while(FsContext->McnDisableCount != 0) { FsContext->McnDisableCount--; ClassEnableMediaChangeDetection(fdoExtension); }
return; }
#if 1
/*
* ISSUE: REMOVE this old function implementation as soon as the * boottime pagefile problems with the new one (below) * are resolved. */ NTSTATUS ClasspEjectionControl( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN MEDIA_LOCK_TYPE LockType, IN BOOLEAN Lock ) { PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = Fdo->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION) FdoExtension; PFILE_OBJECT_EXTENSION fsContext = NULL; NTSTATUS status; PSCSI_REQUEST_BLOCK srb = NULL; BOOLEAN countChanged = FALSE;
PAGED_CODE();
/*
* Ensure that the user thread is not suspended while we are holding EjectSynchronizationEvent. */ KeEnterCriticalRegion();
status = KeWaitForSingleObject( &(FdoExtension->EjectSynchronizationEvent), UserRequest, KernelMode, FALSE, NULL);
ASSERT(status == STATUS_SUCCESS);
DebugPrint((2, "ClasspEjectionControl: " "Received request for %s lock type\n", LockTypeStrings[LockType] ));
try { PCDB cdb;
srb = ClasspAllocateSrb(FdoExtension);
if(srb == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; leave; }
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
cdb = (PCDB) srb->Cdb;
//
// Determine if this is a "secured" request.
//
if(LockType == SecureMediaLock) { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT fileObject = irpStack->FileObject;
//
// Make sure that the file object we are supplied has a
// proper FsContext before we try doing a secured lock.
//
if(fileObject != NULL) { fsContext = ClasspGetFsContext(commonExtension, fileObject); }
if (fsContext == NULL) {
//
// This handle isn't setup correctly. We can't let the
// operation go.
//
status = STATUS_INVALID_PARAMETER; leave; } }
if(Lock) {
//
// This is a lock command. Reissue the command in case bus or
// device was reset and the lock was cleared.
// note: may need to decrement count if actual lock operation
// failed....
//
switch(LockType) {
case SimpleMediaLock: { FdoExtension->LockCount++; countChanged = TRUE; break; }
case SecureMediaLock: { fsContext->LockCount++; FdoExtension->ProtectedLockCount++; countChanged = TRUE; break; }
case InternalMediaLock: { FdoExtension->InternalLockCount++; countChanged = TRUE; break; } } } else {
//
// This is an unlock command. If it's a secured one then make sure
// the caller has a lock outstanding or return an error.
// note: may need to re-increment the count if actual unlock
// operation fails....
//
switch(LockType) {
case SimpleMediaLock: { if(FdoExtension->LockCount != 0) { FdoExtension->LockCount--; countChanged = TRUE; } break; }
case SecureMediaLock: { if(fsContext->LockCount == 0) { status = STATUS_INVALID_DEVICE_STATE; leave; } fsContext->LockCount--; FdoExtension->ProtectedLockCount--; countChanged = TRUE; break; }
case InternalMediaLock: { ASSERT(FdoExtension->InternalLockCount != 0); FdoExtension->InternalLockCount--; countChanged = TRUE; break; } }
//
// We only send an unlock command to the drive if both the
// secured and unsecured lock counts have dropped to zero.
//
if((FdoExtension->ProtectedLockCount != 0) || (FdoExtension->InternalLockCount != 0) || (FdoExtension->LockCount != 0)) {
status = STATUS_SUCCESS; leave; } }
status = STATUS_SUCCESS; if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
srb->CdbLength = 6; cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; //
// TRUE - prevent media removal.
// FALSE - allow media removal.
//
cdb->MEDIA_REMOVAL.Prevent = Lock; //
// Set timeout value.
//
srb->TimeOutValue = FdoExtension->TimeOutValue; //
// The actual lock operation on the device isn't so important
// as the internal lock counts. Ignore failures.
//
status = ClassSendSrbSynchronous(FdoExtension->DeviceObject, srb, NULL, 0, FALSE); }
} finally {
if (!NT_SUCCESS(status)) { DebugPrint((2, "ClasspEjectionControl: FAILED status %x -- " "reverting lock counts\n", status)); if (countChanged) {
//
// have to revert to previous counts if the
// lock/unlock operation actually failed.
//
if(Lock) { switch(LockType) {
case SimpleMediaLock: { FdoExtension->LockCount--; break; }
case SecureMediaLock: { fsContext->LockCount--; FdoExtension->ProtectedLockCount--; break; }
case InternalMediaLock: { FdoExtension->InternalLockCount--; break; } }
} else {
switch(LockType) {
case SimpleMediaLock: { FdoExtension->LockCount++; break; }
case SecureMediaLock: { fsContext->LockCount++; FdoExtension->ProtectedLockCount++; break; }
case InternalMediaLock: { FdoExtension->InternalLockCount++; break; } } }
}
} else {
DebugPrint((2, "ClasspEjectionControl: Succeeded\n"));
}
DebugPrint((2, "ClasspEjectionControl: " "Current Counts: Internal: %x Secure: %x Simple: %x\n", FdoExtension->InternalLockCount, FdoExtension->ProtectedLockCount, FdoExtension->LockCount ));
KeSetEvent(&(FdoExtension->EjectSynchronizationEvent), IO_NO_INCREMENT, FALSE); KeLeaveCriticalRegion(); if (srb) { ClassFreeOrReuseSrb(FdoExtension, srb); }
} return status; }
#else
/*
* ISSUE: RESTORE this (see above) * This is a new implementation of the function that doesn't thrash memory * or depend on the srbLookasideList. * HOWEVER, it seems to cause pagefile initialization to fail during boot * for some reason. Need to resolve this before switching to this function. */ NTSTATUS ClasspEjectionControl( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN MEDIA_LOCK_TYPE LockType, IN BOOLEAN Lock ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; PFILE_OBJECT_EXTENSION fsContext; BOOLEAN fileHandleOk = TRUE; BOOLEAN countChanged = FALSE; NTSTATUS status; PAGED_CODE(); status = KeWaitForSingleObject( &fdoExt->EjectSynchronizationEvent, UserRequest, KernelMode, FALSE, NULL); ASSERT(status == STATUS_SUCCESS);
/*
* If this is a "secured" request, we have to make sure * that the file handle is valid. */ if (LockType == SecureMediaLock){ PIO_STACK_LOCATION thisSp = IoGetCurrentIrpStackLocation(Irp);
/*
* Make sure that the file object we are supplied has a * proper FsContext before we try doing a secured lock. */ if (thisSp->FileObject){ PCOMMON_DEVICE_EXTENSION commonExt = (PCOMMON_DEVICE_EXTENSION)fdoExt; fsContext = ClasspGetFsContext(commonExt, thisSp->FileObject); } else { fsContext = NULL; }
if (!fsContext){ ASSERT(fsContext); fileHandleOk = FALSE; } }
if (fileHandleOk){
/*
* Adjust the lock counts and make sure they make sense. */ status = STATUS_SUCCESS; if (Lock){ switch(LockType) { case SimpleMediaLock: fdoExt->LockCount++; countChanged = TRUE; break; case SecureMediaLock: fsContext->LockCount++; fdoExt->ProtectedLockCount++; countChanged = TRUE; break; case InternalMediaLock: fdoExt->InternalLockCount++; countChanged = TRUE; break; } } else { /*
* This is an unlock command. If it's a secured one then make sure * the caller has a lock outstanding or return an error. */ switch (LockType){ case SimpleMediaLock: if (fdoExt->LockCount > 0){ fdoExt->LockCount--; countChanged = TRUE; } else { ASSERT(fdoExt->LockCount > 0); status = STATUS_INTERNAL_ERROR; } break; case SecureMediaLock: if (fsContext->LockCount > 0){ ASSERT(fdoExt->ProtectedLockCount > 0); fsContext->LockCount--; fdoExt->ProtectedLockCount--; countChanged = TRUE; } else { ASSERT(fsContext->LockCount > 0); status = STATUS_INVALID_DEVICE_STATE; } break; case InternalMediaLock: ASSERT(fdoExt->InternalLockCount > 0); fdoExt->InternalLockCount--; countChanged = TRUE; break; } }
if (NT_SUCCESS(status)){ /*
* We only send an unlock command to the drive if * all the lock counts have dropped to zero. */ if (!Lock && (fdoExt->ProtectedLockCount || fdoExt->InternalLockCount || fdoExt->LockCount)){ /*
* The lock count is still positive, so don't unlock yet. */ status = STATUS_SUCCESS; } else if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { /*
* The device isn't removable media. don't send a cmd. */ status = STATUS_SUCCESS; } else { TRANSFER_PACKET *pkt; pkt = DequeueFreeTransferPacket(Fdo, TRUE); if (pkt){ KEVENT event; /*
* Store the number of packets servicing the irp (one) * inside the original IRP. It will be used to counted down * to zero when the packet completes. * Initialize the original IRP's status to success. * If the packet fails, we will set it to the error status. */ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1); Irp->IoStatus.Status = STATUS_SUCCESS;
/*
* Set this up as a SYNCHRONOUS transfer, submit it, * and wait for the packet to complete. The result * status will be written to the original irp. */ KeInitializeEvent(&event, SynchronizationEvent, FALSE); SetupEjectionTransferPacket(pkt, Lock, &event, Irp); SubmitTransferPacket(pkt); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } } } else { status = STATUS_INVALID_PARAMETER; }
if (!NT_SUCCESS(status) && countChanged) {
//
// have to revert to previous counts if the
// lock/unlock operation actually failed.
//
if(Lock) {
switch(LockType) {
case SimpleMediaLock: { FdoExtension->LockCount--; break; }
case SecureMediaLock: { fsContext->LockCount--; FdoExtension->ProtectedLockCount--; break; }
case InternalMediaLock: { FdoExtension->InternalLockCount--; break; } }
} else {
switch(LockType) {
case SimpleMediaLock: { FdoExtension->LockCount++; break; }
case SecureMediaLock: { fsContext->LockCount++; FdoExtension->ProtectedLockCount++; break; }
case InternalMediaLock: { FdoExtension->InternalLockCount++; break; } } } }
KeSetEvent(&fdoExt->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
return status; } #endif
PFILE_OBJECT_EXTENSION ClasspGetFsContext( IN PCOMMON_DEVICE_EXTENSION CommonExtension, IN PFILE_OBJECT FileObject ) { PAGED_CODE(); return GetDictionaryEntry(&(CommonExtension->FileObjectDictionary), (ULONGLONG) FileObject); }
|