www.pudn.com > filesys.rar > misc.c
/*************************************************************************
*
* File: misc.c
*
* Module: Sample File System Driver (Kernel mode execution only)
*
* Description:
* This file contains some miscellaneous support routines.
*
* Author: R. Nagar
*
* (c) 1996-97 Rajeev Nagar, All Rights Reserved
*
*************************************************************************/
#include "sfsd.h"
// define the file specific bug-check id
#define SFSD_BUG_CHECK_ID SFSD_FILE_MISC
/*************************************************************************
*
* Function: SFsdInitializeZones()
*
* Description:
* Allocates some memory for global zones used to allocate FSD structures.
* Either all memory will be allocated or we will back out gracefully.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS SFsdInitializeZones(
void)
{
NTSTATUS RC = STATUS_SUCCESS;
uint32 SizeOfZone = SFsdGlobalData.DefaultZoneSizeInNumStructs;
uint32 SizeOfObjectNameZone = 0;
uint32 SizeOfCCBZone = 0;
uint32 SizeOfFCBZone = 0;
uint32 SizeOfByteLockZone = 0;
uint32 SizeOfIrpContextZone = 0;
try {
// initialize the spinlock protecting the zones
KeInitializeSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock));
// determine memory requirements
switch (MmQuerySystemSize()) {
case MmSmallSystem:
// this is just for illustration purposes. I will multiply
// number of structures with some arbitrary amount depending
// upon available memory in the system ... You should choose a
// more intelligent method suitable to your memory consumption
// and the amount of memory available.
SizeOfObjectNameZone = (2 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfCCBZone = (2 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdCCB))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfFCBZone = (2 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdFCB))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfByteLockZone = (2 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdFileLockInfo))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfIrpContextZone = (2 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
break;
case MmMediumSystem:
SizeOfObjectNameZone = (4 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfCCBZone = (4 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdCCB))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfFCBZone = (4 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdFCB))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfByteLockZone = (4 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdFileLockInfo))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfIrpContextZone = (4 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
break;
case MmLargeSystem:
SizeOfObjectNameZone = (8 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfCCBZone = (8 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdCCB))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfFCBZone = (8 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdFCB))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfByteLockZone = (8 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdFileLockInfo))) + sizeof(ZONE_SEGMENT_HEADER);
SizeOfIrpContextZone = (8 * SizeOfZone * SFsdQuadAlign(sizeof(SFsdIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
break;
}
// typical NT methodology (at least until *someone* exposed the "difference" between a server and workstation ;-)
if (MmIsThisAnNtAsSystem()) {
SizeOfObjectNameZone *= SFSD_NTAS_MULTIPLE;
SizeOfCCBZone *= SFSD_NTAS_MULTIPLE;
SizeOfFCBZone *= SFSD_NTAS_MULTIPLE;
SizeOfByteLockZone *= SFSD_NTAS_MULTIPLE;
SizeOfIrpContextZone *= SFSD_NTAS_MULTIPLE;
}
// allocate memory for each of the zones and initialize the zones ...
if (!(SFsdGlobalData.ObjectNameZone = ExAllocatePool(NonPagedPool, SizeOfObjectNameZone))) {
RC = STATUS_INSUFFICIENT_RESOURCES;
try_return(RC);
}
if (!(SFsdGlobalData.CCBZone = ExAllocatePool(NonPagedPool, SizeOfCCBZone))) {
RC = STATUS_INSUFFICIENT_RESOURCES;
try_return(RC);
}
if (!(SFsdGlobalData.FCBZone = ExAllocatePool(NonPagedPool, SizeOfFCBZone))) {
RC = STATUS_INSUFFICIENT_RESOURCES;
try_return(RC);
}
if (!(SFsdGlobalData.ByteLockZone = ExAllocatePool(NonPagedPool, SizeOfByteLockZone))) {
RC = STATUS_INSUFFICIENT_RESOURCES;
try_return(RC);
}
if (!(SFsdGlobalData.IrpContextZone = ExAllocatePool(NonPagedPool, SizeOfIrpContextZone))) {
RC = STATUS_INSUFFICIENT_RESOURCES;
try_return(RC);
}
// initialize each of the zone headers ...
if (!NT_SUCCESS(RC = ExInitializeZone(&(SFsdGlobalData.ObjectNameZoneHeader),
SFsdQuadAlign(sizeof(SFsdObjectName)),
SFsdGlobalData.ObjectNameZone, SizeOfObjectNameZone))) {
// failed the initialization, leave ...
try_return(RC);
}
if (!NT_SUCCESS(RC = ExInitializeZone(&(SFsdGlobalData.CCBZoneHeader),
SFsdQuadAlign(sizeof(SFsdCCB)),
SFsdGlobalData.CCBZone,
SizeOfCCBZone))) {
// failed the initialization, leave ...
try_return(RC);
}
if (!NT_SUCCESS(RC = ExInitializeZone(&(SFsdGlobalData.FCBZoneHeader),
SFsdQuadAlign(sizeof(SFsdFCB)),
SFsdGlobalData.FCBZone,
SizeOfFCBZone))) {
// failed the initialization, leave ...
try_return(RC);
}
if (!NT_SUCCESS(RC = ExInitializeZone(&(SFsdGlobalData.ByteLockZoneHeader),
SFsdQuadAlign(sizeof(SFsdFileLockInfo)),
SFsdGlobalData.ByteLockZone,
SizeOfByteLockZone))) {
// failed the initialization, leave ...
try_return(RC);
}
if (!NT_SUCCESS(RC = ExInitializeZone(&(SFsdGlobalData.IrpContextZoneHeader),
SFsdQuadAlign(sizeof(SFsdIrpContext)),
SFsdGlobalData.IrpContextZone,
SizeOfIrpContextZone))) {
// failed the initialization, leave ...
try_return(RC);
}
try_exit: NOTHING;
} finally {
if (!NT_SUCCESS(RC)) {
// invoke the destroy routine now ...
SFsdDestroyZones();
} else {
// mark the fact that we have allocated zones ...
SFsdSetFlag(SFsdGlobalData.SFsdFlags, SFSD_DATA_FLAGS_ZONES_INITIALIZED);
}
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdDestroyZones()
*
* Description:
* Free up the previously allocated memory. NEVER do this once the
* driver has been successfully loaded.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdDestroyZones(
void)
{
try {
// free up each of the pools
ExFreePool(SFsdGlobalData.ObjectNameZone);
ExFreePool(SFsdGlobalData.CCBZone);
ExFreePool(SFsdGlobalData.FCBZone);
ExFreePool(SFsdGlobalData.ByteLockZone);
ExFreePool(SFsdGlobalData.IrpContextZone);
try_exit: NOTHING;
} finally {
SFsdClearFlag(SFsdGlobalData.SFsdFlags, SFSD_DATA_FLAGS_ZONES_INITIALIZED);
}
return;
}
/*************************************************************************
*
* Function: SFsdIsIrpTopLevel()
*
* Description:
* Helps the FSD determine who the "top level" caller is for this
* request. A request can originate directly from a user process
* (in which case, the "top level" will be NULL when this routine
* is invoked), OR the user may have originated either from the NT
* Cache Manager/VMM ("top level" may be set), or this could be a
* recursion into our code in which we would have set the "top level"
* field the last time around.
*
* Expected Interrupt Level (for execution) :
*
* whatever level a particular dispatch routine is invoked at.
*
* Return Value: TRUE/FALSE (TRUE if top level was NULL when routine invoked)
*
*************************************************************************/
BOOLEAN SFsdIsIrpTopLevel(
PIRP Irp) // the IRP sent to our dispatch routine
{
BOOLEAN ReturnCode = FALSE;
if (IoGetTopLevelIrp() == NULL) {
// OK, so we can set ourselves to become the "top level" component
IoSetTopLevelIrp(Irp);
ReturnCode = TRUE;
}
return(ReturnCode);
}
/*************************************************************************
*
* Function: SFsdExceptionFilter()
*
* Description:
* This routines allows the driver to determine whether the exception
* is an "allowed" exception i.e. one we should not-so-quietly consume
* ourselves, or one which should be propagated onwards in which case
* we will most likely bring down the machine.
*
* This routine employs the services of FsRtlIsNtstatusExpected(). This
* routine returns a BOOLEAN result. A RC of FALSE will cause us to return
* EXCEPTION_CONTINUE_SEARCH which will probably cause a panic.
* The FsRtl.. routine returns FALSE iff exception values are (currently) :
* STATUS_DATATYPE_MISALIGNMENT || STATUS_ACCESS_VIOLATION ||
* STATUS_ILLEGAL_INSTRUCTION || STATUS_INSTRUCTION_MISALIGNMENT
*
* Expected Interrupt Level (for execution) :
*
* ?
*
* Return Value: EXCEPTION_EXECUTE_HANDLER/EXECEPTION_CONTINUE_SEARCH
*
*************************************************************************/
long SFsdExceptionFilter(
PtrSFsdIrpContext PtrIrpContext,
PEXCEPTION_POINTERS PtrExceptionPointers)
{
long ReturnCode = EXCEPTION_EXECUTE_HANDLER;
NTSTATUS ExceptionCode = STATUS_SUCCESS;
// figure out the exception code
ExceptionCode = PtrExceptionPointers->ExceptionRecord->ExceptionCode;
if ((ExceptionCode == STATUS_IN_PAGE_ERROR) && (PtrExceptionPointers->ExceptionRecord->NumberParameters >= 3)) {
ExceptionCode = PtrExceptionPointers->ExceptionRecord->ExceptionInformation[2];
}
if (PtrIrpContext) {
PtrIrpContext->SavedExceptionCode = ExceptionCode;
SFsdSetFlag(PtrIrpContext->IrpContextFlags, SFSD_IRP_CONTEXT_EXCEPTION);
}
// check if we should propagate this exception or not
if (!(FsRtlIsNtstatusExpected(ExceptionCode))) {
// we are not ok, propagate this exception.
// NOTE: we will bring down the machine ...
ReturnCode = EXCEPTION_CONTINUE_SEARCH;
// better free up the IrpContext now ...
if (PtrIrpContext) {
SFsdReleaseIrpContext(PtrIrpContext);
}
}
// if you wish to perform some special processing when
// not propagating the exception, set up the state for
// special processing now ...
// return the appropriate code
return(ReturnCode);
}
/*************************************************************************
*
* Function: SFsdExceptionHandler()
*
* Description:
* One of the routines in the FSD or in the modules we invoked encountered
* an exception. We have decided that we will "handle" the exception.
* Therefore we will prevent the machine from a panic ...
* You can do pretty much anything you choose to in your commercial
* driver at this point to ensure a graceful exit. In the sample
* driver, I will simply free up the IrpContext (if any), set the
* error code in the IRP and complete the IRP at this time ...
*
* Expected Interrupt Level (for execution) :
*
* ?
*
* Return Value: Error code
*
*************************************************************************/
NTSTATUS SFsdExceptionHandler(
PtrSFsdIrpContext PtrIrpContext,
PIRP Irp)
{
NTSTATUS RC;
ASSERT(Irp);
if (PtrIrpContext) {
RC = PtrIrpContext->SavedExceptionCode;
// Free irp context here
SFsdReleaseIrpContext(PtrIrpContext);
} else {
// must be insufficient resources ...?
RC = STATUS_INSUFFICIENT_RESOURCES;
}
// set the error code in the IRP
Irp->IoStatus.Status = RC;
Irp->IoStatus.Information = 0;
// complete the IRP
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(RC);
}
/*************************************************************************
*
* Function: SFsdLogEvent()
*
* Description:
* Log a message in the NT Event Log. This is a rather simplistic log
* methodology since you can potentially utilize the event log to
* provide a lot of information to the user (and you should too!)
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdLogEvent(
NTSTATUS SFsdEventLogId, // the SFsd private message id
NTSTATUS RC) // any NT error code we wish to log ...
{
try {
// Implement a call to IoAllocateErrorLogEntry() followed by a call
// to IoWriteErrorLogEntry(). You should note that the call to IoWriteErrorLogEntry()
// will free memory for the entry once the write completes (which in actuality
// is an asynchronous operation).
} except (EXCEPTION_EXECUTE_HANDLER) {
// nothing really we can do here, just do not wish to crash ...
NOTHING;
}
return;
}
/*************************************************************************
*
* Function: SFsdAllocateObjectName()
*
* Description:
* Allocate a new ObjectName structure to represent an open on-disk object.
* Also initialize the ObjectName structure to NULL.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the ObjectName structure OR NULL.
*
*************************************************************************/
PtrSFsdObjectName SFsdAllocateObjectName(
void)
{
PtrSFsdObjectName PtrObjectName = NULL;
BOOLEAN AllocatedFromZone = TRUE;
KIRQL CurrentIrql;
// first, try to allocate out of the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
if (!ExIsFullZone(&(SFsdGlobalData.ObjectNameZoneHeader))) {
// we have enough memory
PtrObjectName = (PtrSFsdObjectName)ExAllocateFromZone(&(SFsdGlobalData.ObjectNameZoneHeader));
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
// if we failed to obtain from the zone, get it directly from the VMM
PtrObjectName = (PtrSFsdObjectName)ExAllocatePool(NonPagedPool, SFsdQuadAlign(sizeof(SFsdObjectName)));
AllocatedFromZone = FALSE;
}
// if we could not obtain the required memory, bug-check.
// Do NOT do this in your commercial driver, instead handle the error gracefully ...
if (!PtrObjectName) {
SFsdPanic(STATUS_INSUFFICIENT_RESOURCES, SFsdQuadAlign(sizeof(SFsdObjectName)), 0);
}
// zero out the allocated memory block
RtlZeroMemory(PtrObjectName, SFsdQuadAlign(sizeof(SFsdObjectName)));
// set up some fields ...
PtrObjectName->NodeIdentifier.NodeType = SFSD_NODE_TYPE_OBJECT_NAME;
PtrObjectName->NodeIdentifier.NodeSize = SFsdQuadAlign(sizeof(SFsdObjectName));
if (!AllocatedFromZone) {
SFsdSetFlag(PtrObjectName->ObjectNameFlags, SFSD_OB_NAME_NOT_FROM_ZONE);
}
return(PtrObjectName);
}
/*************************************************************************
*
* Function: SFsdReleaseObjectName()
*
* Description:
* Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdReleaseObjectName(
PtrSFsdObjectName PtrObjectName)
{
KIRQL CurrentIrql;
ASSERT(PtrObjectName);
// give back memory either to the zone or to the VMM
if (!(PtrObjectName->ObjectNameFlags & SFSD_OB_NAME_NOT_FROM_ZONE)) {
// back to the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
ExFreeToZone(&(SFsdGlobalData.ObjectNameZoneHeader), PtrObjectName);
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
ExFreePool(PtrObjectName);
}
return;
}
/*************************************************************************
*
* Function: SFsdAllocateCCB()
*
* Description:
* Allocate a new CCB structure to represent an open on-disk object.
* Also initialize the CCB structure to NULL.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the CCB structure OR NULL.
*
*************************************************************************/
PtrSFsdCCB SFsdAllocateCCB(
void)
{
PtrSFsdCCB PtrCCB = NULL;
BOOLEAN AllocatedFromZone = TRUE;
KIRQL CurrentIrql;
// first, try to allocate out of the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
if (!ExIsFullZone(&(SFsdGlobalData.CCBZoneHeader))) {
// we have enough memory
PtrCCB = (PtrSFsdCCB)ExAllocateFromZone(&(SFsdGlobalData.CCBZoneHeader));
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
// if we failed to obtain from the zone, get it directly from the VMM
PtrCCB = (PtrSFsdCCB)ExAllocatePool(NonPagedPool, SFsdQuadAlign(sizeof(SFsdCCB)));
AllocatedFromZone = FALSE;
}
// if we could not obtain the required memory, bug-check.
// Do NOT do this in your commercial driver, instead handle the error gracefully ...
if (!PtrCCB) {
SFsdPanic(STATUS_INSUFFICIENT_RESOURCES, SFsdQuadAlign(sizeof(SFsdCCB)), 0);
}
// zero out the allocated memory block
RtlZeroMemory(PtrCCB, SFsdQuadAlign(sizeof(SFsdCCB)));
// set up some fields ...
PtrCCB->NodeIdentifier.NodeType = SFSD_NODE_TYPE_CCB;
PtrCCB->NodeIdentifier.NodeSize = SFsdQuadAlign(sizeof(SFsdCCB));
if (!AllocatedFromZone) {
SFsdSetFlag(PtrCCB->CCBFlags, SFSD_CCB_NOT_FROM_ZONE);
}
return(PtrCCB);
}
/*************************************************************************
*
* Function: SFsdReleaseCCB()
*
* Description:
* Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdReleaseCCB(
PtrSFsdCCB PtrCCB)
{
KIRQL CurrentIrql;
ASSERT(PtrCCB);
// give back memory either to the zone or to the VMM
if (!(PtrCCB->CCBFlags & SFSD_CCB_NOT_FROM_ZONE)) {
// back to the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
ExFreeToZone(&(SFsdGlobalData.CCBZoneHeader), PtrCCB);
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
ExFreePool(PtrCCB);
}
return;
}
/*************************************************************************
*
* Function: SFsdAllocateFCB()
*
* Description:
* Allocate a new FCB structure to represent an open on-disk object.
* Also initialize the FCB structure to NULL.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the FCB structure OR NULL.
*
*************************************************************************/
PtrSFsdFCB SFsdAllocateFCB(
void)
{
PtrSFsdFCB PtrFCB = NULL;
BOOLEAN AllocatedFromZone = TRUE;
KIRQL CurrentIrql;
// first, try to allocate out of the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
if (!ExIsFullZone(&(SFsdGlobalData.FCBZoneHeader))) {
// we have enough memory
PtrFCB = (PtrSFsdFCB)ExAllocateFromZone(&(SFsdGlobalData.FCBZoneHeader));
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
// if we failed to obtain from the zone, get it directly from the VMM
PtrFCB = (PtrSFsdFCB)ExAllocatePool(NonPagedPool, SFsdQuadAlign(sizeof(SFsdFCB)));
AllocatedFromZone = FALSE;
}
// if we could not obtain the required memory, bug-check.
// Do NOT do this in your commercial driver, instead handle the error gracefully ...
if (!PtrFCB) {
SFsdPanic(STATUS_INSUFFICIENT_RESOURCES, SFsdQuadAlign(sizeof(SFsdFCB)), 0);
}
// zero out the allocated memory block
RtlZeroMemory(PtrFCB, SFsdQuadAlign(sizeof(SFsdFCB)));
// set up some fields ...
PtrFCB->NodeIdentifier.NodeType = SFSD_NODE_TYPE_FCB;
PtrFCB->NodeIdentifier.NodeSize = SFsdQuadAlign(sizeof(SFsdFCB));
if (!AllocatedFromZone) {
SFsdSetFlag(PtrFCB->FCBFlags, SFSD_FCB_NOT_FROM_ZONE);
}
return(PtrFCB);
}
/*************************************************************************
*
* Function: SFsdCreateNewFCB()
*
* Description:
* We want to create a new FCB. We will also create a new CCB (presumably)
* later. Simply allocate a new FCB structure and initialize fields
* appropriately.
* This function also takes the file size values that the caller must
* have obtained and will set the file size fields appropriately in the
* CommonFCBHeader.
* Finally, this routine will initialize the FileObject structure passed
* in to this function. If you decide to fail the call later, remember
* to uninitialize the fields.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the FCB structure OR NULL.
*
*************************************************************************/
NTSTATUS SFsdCreateNewFCB(
PtrSFsdFCB *ReturnedFCB,
PLARGE_INTEGER AllocationSize,
PLARGE_INTEGER EndOfFile,
PFILE_OBJECT PtrFileObject,
PtrSFsdVCB PtrVCB)
{
NTSTATUS RC = STATUS_SUCCESS;
PtrSFsdFCB PtrFCB = NULL;
PtrSFsdNTRequiredFCB PtrReqdFCB = NULL;
PFSRTL_COMMON_FCB_HEADER PtrCommonFCBHeader = NULL;
try {
// Obtain a new FCB structure.
// The function SFsdAllocateFCB() will obtain a new structure either
// from a zone or from memory requested directly from the VMM.
PtrFCB = SFsdAllocateFCB();
if (!PtrFCB) {
// Assume lack of memory.
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
// Initialize fields required to interface with the NT Cache Manager.
// Note that the returned structure has already been zeroed. This means
// that the SectionObject structure has been zeroed which is a
// requirement for newly created FCB structures.
PtrReqdFCB = &(PtrFCB->NTRequiredFCB);
// Initialize the MainResource and PagingIoResource structures now.
ExInitializeResourceLite(&(PtrReqdFCB->MainResource));
SFsdSetFlag(PtrFCB->FCBFlags, SFSD_INITIALIZED_MAIN_RESOURCE);
ExInitializeResourceLite(&(PtrReqdFCB->PagingIoResource));
SFsdSetFlag(PtrFCB->FCBFlags, SFSD_INITIALIZED_PAGING_IO_RESOURCE);
// Start initializing the fields contained in the CommonFCBHeader.
PtrCommonFCBHeader = &(PtrReqdFCB->CommonFCBHeader);
// Allow fast-IO for now.
PtrCommonFCBHeader->IsFastIoPossible = FastIoIsPossible;
// Initialize the MainResource and PagingIoResource pointers in
// the CommonFCBHeader structure to point to the ERESOURCE structures we
// have allocated and already initialized above.
PtrCommonFCBHeader->Resource = &(PtrReqdFCB->MainResource);
PtrCommonFCBHeader->PagingIoResource = &(PtrReqdFCB->PagingIoResource);
// Ignore the Flags field in the CommonFCBHeader for now. Part 3
// of the book describes it in greater detail.
// Initialize the file size values here.
PtrCommonFCBHeader->AllocationSize = *(AllocationSize);
PtrCommonFCBHeader->FileSize = *(EndOfFile);
// The following will disable ValidDataLength support. However, your
// FSD may choose to support this concept.
PtrCommonFCBHeader->ValidDataLength.LowPart = 0xFFFFFFFF;
PtrCommonFCBHeader->ValidDataLength.HighPart = 0x7FFFFFFF;
// Initialize other fields for the FCB here ...
PtrFCB->PtrVCB = PtrVCB;
InitializeListHead(&(PtrFCB->NextCCB));
// Initialize fields contained in the file object now.
PtrFileObject->PrivateCacheMap = NULL;
// Note that we could have just as well taken the value of PtrReqdFCB
// directly below. The bottom line however is that the FsContext
// field must point to a FSRTL_COMMON_FCB_HEADER structure.
PtrFileObject->FsContext = (void *)(PtrCommonFCBHeader);
// Other initialization continues here ...
try_exit: NOTHING;
} finally {
;
}
return(RC);
}
/*************************************************************************
*
* Function: SFsdReleaseFCB()
*
* Description:
* Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdReleaseFCB(
PtrSFsdFCB PtrFCB)
{
KIRQL CurrentIrql;
ASSERT(PtrFCB);
// give back memory either to the zone or to the VMM
if (!(PtrFCB->FCBFlags & SFSD_FCB_NOT_FROM_ZONE)) {
// back to the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
ExFreeToZone(&(SFsdGlobalData.FCBZoneHeader), PtrFCB);
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
ExFreePool(PtrFCB);
}
return;
}
/*************************************************************************
*
* Function: SFsdAllocateByteLocks()
*
* Description:
* Allocate a new byte range lock structure and initialize it to NULL.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the SFsdByteLocks structure OR NULL.
*
*************************************************************************/
PtrSFsdFileLockInfo SFsdAllocateByteLocks(
void)
{
PtrSFsdFileLockInfo PtrByteLocks = NULL;
BOOLEAN AllocatedFromZone = TRUE;
KIRQL CurrentIrql;
// first, try to allocate out of the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
if (!ExIsFullZone(&(SFsdGlobalData.ByteLockZoneHeader))) {
// we have enough memory
PtrByteLocks = (PtrSFsdFileLockInfo)ExAllocateFromZone(&(SFsdGlobalData.ByteLockZoneHeader));
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
// if we failed to obtain from the zone, get it directly from the VMM
PtrByteLocks = (PtrSFsdFileLockInfo)ExAllocatePool(NonPagedPool, SFsdQuadAlign(sizeof(SFsdFileLockInfo)));
AllocatedFromZone = FALSE;
}
// if we could not obtain the required memory, bug-check.
// Do NOT do this in your commercial driver, instead handle the error gracefully ...
if (!PtrByteLocks) {
SFsdPanic(STATUS_INSUFFICIENT_RESOURCES, SFsdQuadAlign(sizeof(SFsdFileLockInfo)), 0);
}
// zero out the allocated memory block
RtlZeroMemory(PtrByteLocks, SFsdQuadAlign(sizeof(PtrSFsdFileLockInfo)));
if (!AllocatedFromZone) {
SFsdSetFlag(PtrByteLocks->FileLockFlags, SFSD_BYTE_LOCK_NOT_FROM_ZONE);
}
return(PtrByteLocks);
}
/*************************************************************************
*
* Function: SFsdReleaseByteLocks()
*
* Description:
* Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdReleaseByteLocks(
PtrSFsdFileLockInfo PtrByteLocks)
{
KIRQL CurrentIrql;
ASSERT(PtrByteLocks);
// give back memory either to the zone or to the VMM
if (!(PtrByteLocks->FileLockFlags & SFSD_BYTE_LOCK_NOT_FROM_ZONE)) {
// back to the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
ExFreeToZone(&(SFsdGlobalData.ByteLockZoneHeader), PtrByteLocks);
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
ExFreePool(PtrByteLocks);
}
return;
}
/*************************************************************************
*
* Function: SFsdAllocateIrpContext()
*
* Description:
* The sample FSD creates an IRP context for each request received. This
* routine simply allocates (and initializes to NULL) a SFsdIrpContext
* structure.
* Most of the fields in the context structure are then initialized here.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the IrpContext structure OR NULL.
*
*************************************************************************/
PtrSFsdIrpContext SFsdAllocateIrpContext(
PIRP Irp,
PDEVICE_OBJECT PtrTargetDeviceObject)
{
PtrSFsdIrpContext PtrIrpContext = NULL;
BOOLEAN AllocatedFromZone = TRUE;
KIRQL CurrentIrql;
PIO_STACK_LOCATION PtrIoStackLocation = NULL;
// first, try to allocate out of the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
if (!ExIsFullZone(&(SFsdGlobalData.IrpContextZoneHeader))) {
// we have enough memory
PtrIrpContext = (PtrSFsdIrpContext)ExAllocateFromZone(&(SFsdGlobalData.IrpContextZoneHeader));
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
// release the spinlock
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
// if we failed to obtain from the zone, get it directly from the VMM
PtrIrpContext = (PtrSFsdIrpContext)ExAllocatePool(NonPagedPool, SFsdQuadAlign(sizeof(SFsdIrpContext)));
AllocatedFromZone = FALSE;
}
// if we could not obtain the required memory, bug-check.
// Do NOT do this in your commercial driver, instead handle the error gracefully ...
if (!PtrIrpContext) {
SFsdPanic(STATUS_INSUFFICIENT_RESOURCES, SFsdQuadAlign(sizeof(SFsdIrpContext)), 0);
}
// zero out the allocated memory block
RtlZeroMemory(PtrIrpContext, SFsdQuadAlign(sizeof(SFsdIrpContext)));
// set up some fields ...
PtrIrpContext->NodeIdentifier.NodeType = SFSD_NODE_TYPE_IRP_CONTEXT;
PtrIrpContext->NodeIdentifier.NodeSize = SFsdQuadAlign(sizeof(SFsdIrpContext));
PtrIrpContext->Irp = Irp;
PtrIrpContext->TargetDeviceObject = PtrTargetDeviceObject;
// copy over some fields from the IRP and set appropriate flag values
if (Irp) {
PtrIoStackLocation = IoGetCurrentIrpStackLocation(Irp);
ASSERT(PtrIoStackLocation);
PtrIrpContext->MajorFunction = PtrIoStackLocation->MajorFunction;
PtrIrpContext->MinorFunction = PtrIoStackLocation->MinorFunction;
// Often, a FSD cannot honor a request for asynchronous processing
// of certain critical requests. For example, a "close" request on
// a file object can typically never be deferred. Therefore, do not
// be surprised if sometimes your FSD (just like all other FSD
// implementations on the Windows NT system) has to override the flag
// below.
if (IoIsOperationSynchronous(Irp)) {
SFsdSetFlag(PtrIrpContext->IrpContextFlags, SFSD_IRP_CONTEXT_CAN_BLOCK);
}
}
if (!AllocatedFromZone) {
SFsdSetFlag(PtrIrpContext->IrpContextFlags, SFSD_IRP_CONTEXT_NOT_FROM_ZONE);
}
// Are we top-level ? This information is used by the dispatching code
// later (and also by the FSD dispatch routine)
if (IoGetTopLevelIrp() != Irp) {
// We are not top-level. Note this fact in the context structure
SFsdSetFlag(PtrIrpContext->IrpContextFlags, SFSD_IRP_CONTEXT_NOT_TOP_LEVEL);
}
return(PtrIrpContext);
}
/*************************************************************************
*
* Function: SFsdReleaseIrpContext()
*
* Description:
* Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdReleaseIrpContext(
PtrSFsdIrpContext PtrIrpContext)
{
KIRQL CurrentIrql;
ASSERT(PtrIrpContext);
// give back memory either to the zone or to the VMM
if (!(PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_NOT_FROM_ZONE)) {
// back to the zone
KeAcquireSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
ExFreeToZone(&(SFsdGlobalData.IrpContextZoneHeader), PtrIrpContext);
KeReleaseSpinLock(&(SFsdGlobalData.ZoneAllocationSpinLock), CurrentIrql);
} else {
ExFreePool(PtrIrpContext);
}
return;
}
/*************************************************************************
*
* Function: SFsdPostRequest()
*
* Description:
* Queue up a request for deferred processing (in the context of a system
* worker thread). The caller must have locked the user buffer (if required)
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_PENDING
*
*************************************************************************/
NTSTATUS SFsdPostRequest(
PtrSFsdIrpContext PtrIrpContext,
PIRP PtrIrp)
{
NTSTATUS RC = STATUS_PENDING;
// mark the IRP pending
IoMarkIrpPending(PtrIrp);
// queue up the request
ExInitializeWorkItem(&(PtrIrpContext->WorkQueueItem), SFsdCommonDispatch, PtrIrpContext);
ExQueueWorkItem(&(PtrIrpContext->WorkQueueItem), CriticalWorkQueue);
// return status pending
return(RC);
}
/*************************************************************************
*
* Function: SFsdCommonDispatch()
*
* Description:
* The common dispatch routine invoked in the context of a system worker
* thread. All we do here is pretty much case off the major function
* code and invoke the appropriate FSD dispatch routine for further
* processing.
*
* Expected Interrupt Level (for execution) :
*
* IRQL PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdCommonDispatch(
void *Context) // actually an IRPContext structure
{
NTSTATUS RC = STATUS_SUCCESS;
PtrSFsdIrpContext PtrIrpContext = NULL;
PIRP PtrIrp = NULL;
// The context must be a pointer to an IrpContext structure
PtrIrpContext = (PtrSFsdIrpContext)Context;
ASSERT(PtrIrpContext);
// Assert that the Context is legitimate
if ((PtrIrpContext->NodeIdentifier.NodeType != SFSD_NODE_TYPE_IRP_CONTEXT) || (PtrIrpContext->NodeIdentifier.NodeSize != SFsdQuadAlign(sizeof(SFsdIrpContext)))) {
// This does not look good
SFsdPanic(SFSD_ERROR_INTERNAL_ERROR, PtrIrpContext->NodeIdentifier.NodeType, PtrIrpContext->NodeIdentifier.NodeSize);
}
// Get a pointer to the IRP structure
PtrIrp = PtrIrpContext->Irp;
ASSERT(PtrIrp);
// Now, check if the FSD was top level when the IRP was originally invoked
// and set the thread context (for the worker thread) appropriately
if (PtrIrpContext->IrpContextFlags & SFSD_IRP_CONTEXT_NOT_TOP_LEVEL) {
// The FSD is not top level for the original request
// Set a constant value in TLS to reflect this fact
IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
}
// Since the FSD routine will now be invoked in the context of this worker
// thread, we should inform the FSD that it is perfectly OK to block in
// the context of this thread
SFsdSetFlag(PtrIrpContext->IrpContextFlags, SFSD_IRP_CONTEXT_CAN_BLOCK);
FsRtlEnterFileSystem();
try {
// Pre-processing has been completed; check the Major Function code value
// either in the IrpContext (copied from the IRP), or directly from the
// IRP itself (we will need a pointer to the stack location to do that),
// Then, switch based on the value on the Major Function code
switch (PtrIrpContext->MajorFunction) {
case IRP_MJ_CREATE:
// Invoke the common create routine
(void)SFsdCommonCreate(PtrIrpContext, PtrIrp);
break;
case IRP_MJ_READ:
// Invoke the common read routine
(void)SFsdCommonRead(PtrIrpContext, PtrIrp);
break;
// Continue with the remaining possible dispatch routines below ...
default:
// This is the case where we have an invalid major function
PtrIrp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
PtrIrp->IoStatus.Information = 0;
IoCompleteRequest(PtrIrp, IO_NO_INCREMENT);
break;
}
} except (SFsdExceptionFilter(PtrIrpContext, GetExceptionInformation())) {
RC = SFsdExceptionHandler(PtrIrpContext, PtrIrp);
SFsdLogEvent(SFSD_ERROR_INTERNAL_ERROR, RC);
}
// Enable preemption
FsRtlExitFileSystem();
// Ensure that the "top-level" field is cleared
IoSetTopLevelIrp(NULL);
return;
}
/*************************************************************************
*
* Function: SFsdInitializeVCB()
*
* Description:
* Perform the initialization for a VCB structure.
*
* Expected Interrupt Level (for execution) :
*
* IRQL PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
void SFsdInitializeVCB(
PDEVICE_OBJECT PtrVolumeDeviceObject,
PDEVICE_OBJECT PtrTargetDeviceObject,
PVPB PtrVPB)
{
NTSTATUS RC = STATUS_SUCCESS;
PtrSFsdVCB PtrVCB = NULL;
BOOLEAN VCBResourceInitialized = FALSE;
PtrVCB = (PtrSFsdVCB)(PtrVolumeDeviceObject->DeviceExtension);
// Zero it out (typically this has already been done by the I/O
// Manager but it does not hurt to do it again)!
RtlZeroMemory(PtrVCB, sizeof(SFsdVCB));
// Initialize the signature fields
PtrVCB->NodeIdentifier.NodeType = SFSD_NODE_TYPE_VCB;
PtrVCB->NodeIdentifier.NodeSize = sizeof(SFsdVCB);
// Initialize the ERESOURCE object.
RC = ExInitializeResourceLite(&(PtrVCB->VCBResource));
ASSERT(NT_SUCCESS(RC));
VCBResourceInitialized = TRUE;
// We know the target device object.
// Note that this is not neccessarily a pointer to the actual
// physical/virtual device on which the logical volume should
// be mounted. This is actually a pointer to either the actual
// (real) device or to any device object that may have been
// attached to it. Any IRPs that we send down should be sent to this
// device object. However, the "real" physical/virtual device object
// on which we perform our mount operation can be determined from the
// RealDevice field in the VPB sent to us.
PtrVCB->TargetDeviceObject = PtrTargetDeviceObject;
// We also have a pointer to the newly created device object representing
// this logical volume (remember that this VCB structure is simply an
// extension of the created device object).
PtrVCB->VCBDeviceObject = PtrVolumeDeviceObject;
// We also have the VPB pointer. This was obtained from the
// Parameters.MountVolume.Vpb field in the current I/O stack location
// for the mount IRP.
PtrVCB->PtrVPB = PtrVPB;
// Initialize the list anchor (head) for some lists in this VCB.
InitializeListHead(&(PtrVCB->NextFCB));
InitializeListHead(&(PtrVCB->NextNotifyIRP));
InitializeListHead(&(PtrVCB->VolumeOpenListHead));
// Initialize the notify IRP list mutex
KeInitializeMutex(&(PtrVCB->NotifyIRPMutex), 0);
// Set the initial file size values appropriately. Note that your FSD may
// wish to guess at the initial amount of information you would like to
// read from the disk until you have really determined that this a valid
// logical volume (on disk) that you wish to mount.
// PtrVCB->FileSize = PtrVCB->AllocationSize = ??
// You typically do not want to bother with valid data length callbacks
// from the Cache Manager for the file stream opened for volume metadata
// information
PtrVCB->ValidDataLength.LowPart = 0xFFFFFFFF;
PtrVCB->ValidDataLength.HighPart = 0x7FFFFFFF;
// Create a stream file object for this volume.
PtrVCB->PtrStreamFileObject = IoCreateStreamFileObject(NULL,
PtrVCB->PtrVPB->RealDevice);
ASSERT(PtrVCB->PtrStreamFileObject);
// Initialize some important fields in the newly created file object.
PtrVCB->PtrStreamFileObject->FsContext = (void *)PtrVCB;
PtrVCB->PtrStreamFileObject->FsContext2 = NULL;
PtrVCB->PtrStreamFileObject->SectionObjectPointer = &(PtrVCB->SectionObject);
PtrVCB->PtrStreamFileObject->Vpb = PtrVPB;
// Link this chap onto the global linked list of all VCB structures.
ExAcquireResourceExclusiveLite(&(SFsdGlobalData.GlobalDataResource), TRUE);
InsertTailList(&(SFsdGlobalData.NextVCB), &(PtrVCB->NextVCB));
// Initialize caching for the stream file object.
CcInitializeCacheMap(PtrVCB->PtrStreamFileObject, (PCC_FILE_SIZES)(&(PtrVCB->AllocationSize)),
TRUE, // We will use pinned access.
&(SFsdGlobalData.CacheMgrCallBacks), PtrVCB);
SFsdReleaseResource(&(SFsdGlobalData.GlobalDataResource));
// Mark the fact that this VCB structure is initialized.
SFsdSetFlag(PtrVCB->VCBFlags, SFSD_VCB_FLAGS_VCB_INITIALIZED);
return;
}