mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-01 04:13:35 +00:00
2122 lines
60 KiB
C
2122 lines
60 KiB
C
/*************************************************************************
|
|
*
|
|
* File: misc.c
|
|
*
|
|
* Module: Ext2 File System Driver (Kernel mode execution only)
|
|
*
|
|
* Description:
|
|
* This file contains some miscellaneous support routines.
|
|
*
|
|
* Author: Manoj Paul Joseph
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include "ext2fsd.h"
|
|
|
|
// define the file specific bug-check id
|
|
#define EXT2_BUG_CHECK_ID EXT2_FILE_MISC
|
|
|
|
#define DEBUG_LEVEL ( DEBUG_TRACE_MISC )
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2InitializeZones()
|
|
*
|
|
* 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 NTAPI Ext2InitializeZones(
|
|
void)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
uint32 SizeOfZone = Ext2GlobalData.DefaultZoneSizeInNumStructs;
|
|
uint32 SizeOfObjectNameZone = 0;
|
|
uint32 SizeOfCCBZone = 0;
|
|
uint32 SizeOfFCBZone = 0;
|
|
uint32 SizeOfByteLockZone = 0;
|
|
uint32 SizeOfIrpContextZone = 0;
|
|
|
|
try {
|
|
|
|
// initialize the spinlock protecting the zones
|
|
KeInitializeSpinLock(&(Ext2GlobalData.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 * Ext2QuadAlign(sizeof(Ext2ObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfCCBZone = (2 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2CCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfFCBZone = (2 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2FCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfByteLockZone = (2 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2FileLockInfo))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfIrpContextZone = (2 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2IrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
break;
|
|
case MmMediumSystem:
|
|
SizeOfObjectNameZone = (4 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2ObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfCCBZone = (4 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2CCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfFCBZone = (4 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2FCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfByteLockZone = (4 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2FileLockInfo))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfIrpContextZone = (4 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2IrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
break;
|
|
case MmLargeSystem:
|
|
SizeOfObjectNameZone = (8 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2ObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfCCBZone = (8 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2CCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfFCBZone = (8 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2FCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfByteLockZone = (8 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2FileLockInfo))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfIrpContextZone = (8 * SizeOfZone * Ext2QuadAlign(sizeof(Ext2IrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
break;
|
|
}
|
|
|
|
// typical NT methodology (at least until *someone* exposed the "difference" between a server and workstation ;-)
|
|
if (MmIsThisAnNtAsSystem()) {
|
|
SizeOfObjectNameZone *= EXT2_NTAS_MULTIPLE;
|
|
SizeOfCCBZone *= EXT2_NTAS_MULTIPLE;
|
|
SizeOfFCBZone *= EXT2_NTAS_MULTIPLE;
|
|
SizeOfByteLockZone *= EXT2_NTAS_MULTIPLE;
|
|
SizeOfIrpContextZone *= EXT2_NTAS_MULTIPLE;
|
|
}
|
|
|
|
// allocate memory for each of the zones and initialize the zones ...
|
|
if (!(Ext2GlobalData.ObjectNameZone = Ext2AllocatePool(NonPagedPool, SizeOfObjectNameZone ))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return();
|
|
}
|
|
|
|
if (!(Ext2GlobalData.CCBZone = Ext2AllocatePool(NonPagedPool, SizeOfCCBZone ))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return();
|
|
}
|
|
|
|
if (!(Ext2GlobalData.FCBZone = Ext2AllocatePool(NonPagedPool, SizeOfFCBZone ))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return();
|
|
}
|
|
|
|
if (!(Ext2GlobalData.ByteLockZone = Ext2AllocatePool(NonPagedPool, SizeOfByteLockZone ))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return();
|
|
}
|
|
|
|
if (!(Ext2GlobalData.IrpContextZone = Ext2AllocatePool(NonPagedPool, SizeOfIrpContextZone ))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return();
|
|
}
|
|
|
|
// initialize each of the zone headers ...
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(Ext2GlobalData.ObjectNameZoneHeader),
|
|
Ext2QuadAlign(sizeof(Ext2ObjectName)),
|
|
Ext2GlobalData.ObjectNameZone, SizeOfObjectNameZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return();
|
|
}
|
|
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(Ext2GlobalData.CCBZoneHeader),
|
|
Ext2QuadAlign(sizeof(Ext2CCB)),
|
|
Ext2GlobalData.CCBZone,
|
|
SizeOfCCBZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return();
|
|
}
|
|
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(Ext2GlobalData.FCBZoneHeader),
|
|
Ext2QuadAlign(sizeof(Ext2FCB)),
|
|
Ext2GlobalData.FCBZone,
|
|
SizeOfFCBZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return();
|
|
}
|
|
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(Ext2GlobalData.ByteLockZoneHeader),
|
|
Ext2QuadAlign(sizeof(Ext2FileLockInfo)),
|
|
Ext2GlobalData.ByteLockZone,
|
|
SizeOfByteLockZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return();
|
|
}
|
|
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(Ext2GlobalData.IrpContextZoneHeader),
|
|
Ext2QuadAlign(sizeof(Ext2IrpContext)),
|
|
Ext2GlobalData.IrpContextZone,
|
|
SizeOfIrpContextZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return();
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} finally {
|
|
if (!NT_SUCCESS(RC)) {
|
|
// invoke the destroy routine now ...
|
|
Ext2DestroyZones();
|
|
} else {
|
|
// mark the fact that we have allocated zones ...
|
|
Ext2SetFlag(Ext2GlobalData.Ext2Flags, EXT2_DATA_FLAGS_ZONES_INITIALIZED);
|
|
}
|
|
}
|
|
|
|
return(RC);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2DestroyZones()
|
|
*
|
|
* 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 NTAPI Ext2DestroyZones(
|
|
void)
|
|
{
|
|
try {
|
|
// free up each of the pools
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", Ext2GlobalData.ObjectNameZone);
|
|
ExFreePool(Ext2GlobalData.ObjectNameZone);
|
|
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", Ext2GlobalData.CCBZone);
|
|
ExFreePool(Ext2GlobalData.CCBZone);
|
|
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", Ext2GlobalData.FCBZone);
|
|
ExFreePool(Ext2GlobalData.FCBZone);
|
|
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", Ext2GlobalData.ByteLockZone);
|
|
ExFreePool(Ext2GlobalData.ByteLockZone);
|
|
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", Ext2GlobalData.IrpContextZone);
|
|
ExFreePool(Ext2GlobalData.IrpContextZone);
|
|
}
|
|
finally
|
|
{
|
|
Ext2ClearFlag(Ext2GlobalData.Ext2Flags, EXT2_DATA_FLAGS_ZONES_INITIALIZED);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2IsIrpTopLevel()
|
|
*
|
|
* 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 NTAPI Ext2IsIrpTopLevel(
|
|
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: Ext2ExceptionFilter()
|
|
*
|
|
* 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 NTAPI Ext2ExceptionFilter(
|
|
PtrExt2IrpContext 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;
|
|
Ext2SetFlag(PtrIrpContext->IrpContextFlags, EXT2_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)
|
|
{
|
|
Ext2ReleaseIrpContext(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: Ext2ExceptionHandler()
|
|
*
|
|
* 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 NTAPI Ext2ExceptionHandler(
|
|
PtrExt2IrpContext PtrIrpContext,
|
|
PIRP Irp)
|
|
{
|
|
NTSTATUS RC;
|
|
|
|
ASSERT(Irp);
|
|
|
|
if (PtrIrpContext)
|
|
{
|
|
RC = PtrIrpContext->SavedExceptionCode;
|
|
// Free irp context here
|
|
Ext2ReleaseIrpContext(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: Ext2LogEvent()
|
|
*
|
|
* 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 NTAPI Ext2LogEvent(
|
|
NTSTATUS Ext2EventLogId, // the Ext2 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: Ext2AllocateObjectName()
|
|
*
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrExt2ObjectName NTAPI Ext2AllocateObjectName(
|
|
void)
|
|
{
|
|
PtrExt2ObjectName PtrObjectName = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
//KIRQL CurrentIrql;
|
|
/*
|
|
// first, try to allocate out of the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(Ext2GlobalData.ObjectNameZoneHeader))) {
|
|
// we have enough memory
|
|
PtrObjectName = (PtrExt2ObjectName)ExAllocateFromZone(&(Ext2GlobalData.ObjectNameZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
*/
|
|
PtrObjectName = (PtrExt2ObjectName)Ext2AllocatePool(NonPagedPool, Ext2QuadAlign(sizeof(Ext2ObjectName)) );
|
|
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)
|
|
{
|
|
Ext2Panic(STATUS_INSUFFICIENT_RESOURCES, Ext2QuadAlign(sizeof(Ext2ObjectName)), 0);
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory( PtrObjectName, Ext2QuadAlign(sizeof(Ext2ObjectName)) );
|
|
|
|
// set up some fields ...
|
|
PtrObjectName->NodeIdentifier.NodeType = EXT2_NODE_TYPE_OBJECT_NAME;
|
|
PtrObjectName->NodeIdentifier.NodeSize = Ext2QuadAlign(sizeof(Ext2ObjectName));
|
|
|
|
|
|
if (!AllocatedFromZone)
|
|
{
|
|
Ext2SetFlag(PtrObjectName->ObjectNameFlags, EXT2_OB_NAME_NOT_FROM_ZONE);
|
|
}
|
|
|
|
return(PtrObjectName);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2ReleaseObjectName()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
void NTAPI Ext2ReleaseObjectName(
|
|
PtrExt2ObjectName PtrObjectName)
|
|
{
|
|
#ifdef USE_ZONES
|
|
KIRQL CurrentIrql;
|
|
#endif
|
|
|
|
ASSERT(PtrObjectName);
|
|
PtrObjectName->NodeIdentifier.NodeType = EXT2_NODE_TYPE_FREED;
|
|
#ifdef USE_ZONES
|
|
|
|
// give back memory either to the zone or to the VMM
|
|
if (!(PtrObjectName->ObjectNameFlags & EXT2_OB_NAME_NOT_FROM_ZONE))
|
|
{
|
|
// back to the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(Ext2GlobalData.ObjectNameZoneHeader), PtrObjectName);
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
|
|
Ext2DeallocateUnicodeString( & PtrObjectName->ObjectName );
|
|
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", PtrObjectName);
|
|
ExFreePool(PtrObjectName);
|
|
|
|
#ifdef USE_ZONES
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2AllocateCCB()
|
|
*
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrExt2CCB NTAPI Ext2AllocateCCB(
|
|
void)
|
|
{
|
|
PtrExt2CCB PtrCCB = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
#ifdef USE_ZONES
|
|
KIRQL CurrentIrql;
|
|
#endif
|
|
|
|
|
|
#ifdef USE_ZONES
|
|
// first, try to allocate out of the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(Ext2GlobalData.CCBZoneHeader)))
|
|
{
|
|
// we have enough memory
|
|
PtrCCB = (PtrExt2CCB)ExAllocateFromZone(&(Ext2GlobalData.CCBZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
}
|
|
else
|
|
{
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
#endif
|
|
|
|
PtrCCB = (PtrExt2CCB)Ext2AllocatePool(NonPagedPool, Ext2QuadAlign(sizeof(Ext2CCB)) );
|
|
AllocatedFromZone = FALSE;
|
|
|
|
#ifdef USE_ZONES
|
|
}
|
|
#endif
|
|
|
|
// 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)
|
|
{
|
|
Ext2Panic(STATUS_INSUFFICIENT_RESOURCES, Ext2QuadAlign(sizeof(Ext2CCB)), 0);
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(PtrCCB, Ext2QuadAlign(sizeof(Ext2CCB)));
|
|
|
|
// set up some fields ...
|
|
PtrCCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_CCB;
|
|
PtrCCB->NodeIdentifier.NodeSize = Ext2QuadAlign(sizeof(Ext2CCB));
|
|
|
|
|
|
if (!AllocatedFromZone)
|
|
{
|
|
Ext2SetFlag(PtrCCB->CCBFlags, EXT2_CCB_NOT_FROM_ZONE);
|
|
}
|
|
|
|
return(PtrCCB);
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2ReleaseCCB()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
void NTAPI Ext2ReleaseCCB(
|
|
PtrExt2CCB PtrCCB)
|
|
{
|
|
#ifdef USE_ZONES
|
|
KIRQL CurrentIrql;
|
|
#endif
|
|
|
|
ASSERT( PtrCCB );
|
|
if(PtrCCB->NodeIdentifier.NodeType != EXT2_NODE_TYPE_CCB)
|
|
{
|
|
Ext2Panic( PtrCCB, PtrCCB->NodeIdentifier.NodeType, EXT2_NODE_TYPE_CCB ) ;
|
|
}
|
|
|
|
Ext2DeallocateUnicodeString( &PtrCCB->DirectorySearchPattern );
|
|
Ext2DeallocateUnicodeString( &PtrCCB->AbsolutePathName );
|
|
Ext2DeallocateUnicodeString( &PtrCCB->RenameLinkTargetFileName );
|
|
|
|
|
|
#ifdef USE_ZONES
|
|
|
|
// give back memory either to the zone or to the VMM
|
|
if (!(PtrCCB->CCBFlags & EXT2_CCB_NOT_FROM_ZONE))
|
|
{
|
|
// back to the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(Ext2GlobalData.CCBZoneHeader), PtrCCB);
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
PtrCCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_FREED;
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", PtrCCB);
|
|
ExFreePool(PtrCCB);
|
|
|
|
#ifdef USE_ZONES
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2AllocateFCB()
|
|
*
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrExt2FCB NTAPI Ext2AllocateFCB(
|
|
void)
|
|
{
|
|
PtrExt2FCB PtrFCB = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
#ifdef USE_ZONES
|
|
KIRQL CurrentIrql;
|
|
#endif
|
|
|
|
// first, try to allocate out of the zone
|
|
#ifdef USE_ZONES
|
|
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(Ext2GlobalData.FCBZoneHeader))) {
|
|
// we have enough memory
|
|
PtrFCB = (PtrExt2FCB)ExAllocateFromZone(&(Ext2GlobalData.FCBZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
#endif
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
PtrFCB = (PtrExt2FCB)Ext2AllocatePool(NonPagedPool, Ext2QuadAlign(sizeof(Ext2FCB)) );
|
|
AllocatedFromZone = FALSE;
|
|
|
|
#ifdef USE_ZONES
|
|
}
|
|
#endif
|
|
|
|
// 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)
|
|
{
|
|
Ext2Panic(STATUS_INSUFFICIENT_RESOURCES, Ext2QuadAlign(sizeof(Ext2FCB)), 0);
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(PtrFCB, Ext2QuadAlign(sizeof(Ext2FCB)));
|
|
|
|
// set up some fields ...
|
|
PtrFCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_FCB;
|
|
PtrFCB->NodeIdentifier.NodeSize = Ext2QuadAlign(sizeof(Ext2FCB));
|
|
|
|
|
|
if (!AllocatedFromZone)
|
|
{
|
|
Ext2SetFlag(PtrFCB->FCBFlags, EXT2_FCB_NOT_FROM_ZONE);
|
|
}
|
|
|
|
return(PtrFCB);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2CreateNewFCB()
|
|
*
|
|
* 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 NTAPI Ext2CreateNewFCB(
|
|
PtrExt2FCB *ReturnedFCB,
|
|
LARGE_INTEGER AllocationSize,
|
|
LARGE_INTEGER EndOfFile,
|
|
PFILE_OBJECT PtrFileObject,
|
|
PtrExt2VCB PtrVCB,
|
|
PtrExt2ObjectName PtrObjectName)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
|
|
PtrExt2FCB PtrFCB = NULL;
|
|
PtrExt2NTRequiredFCB PtrReqdFCB = NULL;
|
|
PFSRTL_COMMON_FCB_HEADER PtrCommonFCBHeader = NULL;
|
|
|
|
ASSERT( PtrVCB );
|
|
|
|
try
|
|
{
|
|
if( !PtrFileObject )
|
|
{
|
|
PtrFCB = Ext2GetUsedFCB( PtrVCB );
|
|
|
|
}
|
|
else
|
|
{
|
|
// Obtain a new FCB structure.
|
|
// The function Ext2AllocateFCB() will obtain a new structure either
|
|
// from a zone or from memory requested directly from the VMM.
|
|
PtrFCB = Ext2AllocateFCB();
|
|
}
|
|
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));
|
|
Ext2SetFlag(PtrFCB->FCBFlags, EXT2_INITIALIZED_MAIN_RESOURCE);
|
|
|
|
ExInitializeResourceLite(&(PtrReqdFCB->PagingIoResource));
|
|
Ext2SetFlag(PtrFCB->FCBFlags, EXT2_INITIALIZED_PAGING_IO_RESOURCE);
|
|
|
|
// Start initializing the fields contained in the CommonFCBHeader.
|
|
PtrCommonFCBHeader = &(PtrReqdFCB->CommonFCBHeader);
|
|
|
|
// Disallow fast-IO for now.
|
|
PtrCommonFCBHeader->IsFastIoPossible = FastIoIsNotPossible;
|
|
|
|
// 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;
|
|
|
|
// caller MUST ensure that VCB has been acquired exclusively
|
|
InsertTailList(&(PtrVCB->FCBListHead), &(PtrFCB->NextFCB));
|
|
|
|
|
|
InitializeListHead(&(PtrFCB->CCBListHead));
|
|
|
|
// Initialize fields contained in the file object now.
|
|
if( PtrFileObject )
|
|
{
|
|
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);
|
|
PtrFileObject->SectionObjectPointer = &(PtrFCB->NTRequiredFCB.SectionObject) ;
|
|
}
|
|
|
|
// Initialising the object name...
|
|
PtrFCB->FCBName = PtrObjectName;
|
|
|
|
// Returning the FCB...
|
|
*ReturnedFCB = PtrFCB;
|
|
try_exit: NOTHING;
|
|
}
|
|
|
|
finally
|
|
{
|
|
|
|
}
|
|
|
|
return(RC);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2ReleaseFCB()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
void NTAPI Ext2ReleaseFCB(
|
|
PtrExt2FCB PtrFCB)
|
|
{
|
|
//KIRQL CurrentIrql;
|
|
|
|
AssertFCB( PtrFCB );
|
|
|
|
if( PtrFCB->NodeIdentifier.NodeType != EXT2_NODE_TYPE_FCB )
|
|
{
|
|
Ext2Panic( PtrFCB, PtrFCB->NodeIdentifier.NodeType, EXT2_NODE_TYPE_FCB ) ;
|
|
}
|
|
|
|
|
|
PtrFCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_FREED;
|
|
|
|
/*
|
|
// give back memory either to the zone or to the VMM
|
|
if (!(PtrFCB->FCBFlags & EXT2_FCB_NOT_FROM_ZONE))
|
|
{
|
|
// back to the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(Ext2GlobalData.FCBZoneHeader), PtrFCB);
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
}
|
|
else
|
|
{
|
|
*/
|
|
|
|
ExDeleteResourceLite( &PtrFCB->NTRequiredFCB.MainResource );
|
|
ExDeleteResourceLite( &PtrFCB->NTRequiredFCB.PagingIoResource );
|
|
|
|
RemoveEntryList(&(PtrFCB->NextFCB));
|
|
|
|
if( PtrFCB->FCBName )
|
|
{
|
|
Ext2ReleaseObjectName( PtrFCB->FCBName );
|
|
}
|
|
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", PtrFCB);
|
|
ExFreePool(PtrFCB);
|
|
|
|
/*
|
|
}
|
|
*/
|
|
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2AllocateByteLocks()
|
|
*
|
|
* 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 Ext2ByteLocks structure OR NULL.
|
|
*
|
|
*************************************************************************/
|
|
PtrExt2FileLockInfo NTAPI Ext2AllocateByteLocks(
|
|
void)
|
|
{
|
|
PtrExt2FileLockInfo PtrByteLocks = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
KIRQL CurrentIrql;
|
|
|
|
// first, try to allocate out of the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(Ext2GlobalData.ByteLockZoneHeader)))
|
|
{
|
|
// we have enough memory
|
|
PtrByteLocks = (PtrExt2FileLockInfo)ExAllocateFromZone(&(Ext2GlobalData.ByteLockZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
}
|
|
else
|
|
{
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
PtrByteLocks = (PtrExt2FileLockInfo)Ext2AllocatePool(NonPagedPool, Ext2QuadAlign(sizeof(Ext2FileLockInfo)) );
|
|
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)
|
|
{
|
|
Ext2Panic(STATUS_INSUFFICIENT_RESOURCES, Ext2QuadAlign(sizeof(Ext2FileLockInfo)), 0);
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(PtrByteLocks, Ext2QuadAlign(sizeof(PtrExt2FileLockInfo)));
|
|
|
|
if (!AllocatedFromZone)
|
|
{
|
|
Ext2SetFlag(PtrByteLocks->FileLockFlags, EXT2_BYTE_LOCK_NOT_FROM_ZONE);
|
|
}
|
|
|
|
return(PtrByteLocks);
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2ReleaseByteLocks()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
void NTAPI Ext2ReleaseByteLocks(
|
|
PtrExt2FileLockInfo PtrByteLocks)
|
|
{
|
|
KIRQL CurrentIrql;
|
|
|
|
ASSERT(PtrByteLocks);
|
|
|
|
// give back memory either to the zone or to the VMM
|
|
if (!(PtrByteLocks->FileLockFlags & EXT2_BYTE_LOCK_NOT_FROM_ZONE)) {
|
|
// back to the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(Ext2GlobalData.ByteLockZoneHeader), PtrByteLocks);
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
}
|
|
else
|
|
{
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", PtrByteLocks);
|
|
ExFreePool(PtrByteLocks);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2AllocateIrpContext()
|
|
*
|
|
* Description:
|
|
* The sample FSD creates an IRP context for each request received. This
|
|
* routine simply allocates (and initializes to NULL) a Ext2IrpContext
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrExt2IrpContext NTAPI Ext2AllocateIrpContext(
|
|
PIRP Irp,
|
|
PDEVICE_OBJECT PtrTargetDeviceObject)
|
|
{
|
|
PtrExt2IrpContext PtrIrpContext = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
//KIRQL CurrentIrql;
|
|
PIO_STACK_LOCATION PtrIoStackLocation = NULL;
|
|
|
|
/*
|
|
// Allocation from zone not done at present...
|
|
|
|
// first, try to allocate out of the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(Ext2GlobalData.IrpContextZoneHeader))) {
|
|
// we have enough memory
|
|
PtrIrpContext = (PtrExt2IrpContext)ExAllocateFromZone(&(Ext2GlobalData.IrpContextZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
|
|
|
|
|
|
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
PtrIrpContext = (PtrExt2IrpContext)Ext2AllocatePool(NonPagedPool, Ext2QuadAlign(sizeof(Ext2IrpContext)) );
|
|
AllocatedFromZone = FALSE;
|
|
}
|
|
|
|
//No Zone handling for now
|
|
*/
|
|
|
|
PtrIrpContext = (PtrExt2IrpContext)Ext2AllocatePool(NonPagedPool, Ext2QuadAlign(sizeof(Ext2IrpContext)) );
|
|
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)
|
|
{
|
|
Ext2Panic(STATUS_INSUFFICIENT_RESOURCES, Ext2QuadAlign(sizeof(Ext2IrpContext)), 0);
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(PtrIrpContext, Ext2QuadAlign(sizeof(Ext2IrpContext)));
|
|
|
|
// set up some fields ...
|
|
PtrIrpContext->NodeIdentifier.NodeType = EXT2_NODE_TYPE_IRP_CONTEXT;
|
|
PtrIrpContext->NodeIdentifier.NodeSize = Ext2QuadAlign(sizeof(Ext2IrpContext));
|
|
|
|
|
|
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( PtrIoStackLocation->FileObject )
|
|
{
|
|
if (IoIsOperationSynchronous(Irp) )
|
|
{
|
|
Ext2SetFlag(PtrIrpContext->IrpContextFlags, EXT2_IRP_CONTEXT_CAN_BLOCK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Ext2SetFlag(PtrIrpContext->IrpContextFlags, EXT2_IRP_CONTEXT_CAN_BLOCK);
|
|
}
|
|
}
|
|
|
|
if (!AllocatedFromZone)
|
|
{
|
|
Ext2SetFlag(PtrIrpContext->IrpContextFlags, EXT2_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
|
|
Ext2SetFlag(PtrIrpContext->IrpContextFlags, EXT2_IRP_CONTEXT_NOT_TOP_LEVEL);
|
|
}
|
|
|
|
InitializeListHead( &PtrIrpContext->SavedBCBsListHead );
|
|
|
|
return(PtrIrpContext);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2ReleaseIrpContext()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
void NTAPI Ext2ReleaseIrpContext(
|
|
PtrExt2IrpContext PtrIrpContext)
|
|
{
|
|
KIRQL CurrentIrql;
|
|
|
|
ASSERT(PtrIrpContext);
|
|
|
|
// Flush the saved BCBs...
|
|
Ext2FlushSavedBCBs( PtrIrpContext );
|
|
|
|
// give back memory either to the zone or to the VMM
|
|
if (!(PtrIrpContext->IrpContextFlags & EXT2_IRP_CONTEXT_NOT_FROM_ZONE))
|
|
{
|
|
// back to the zone
|
|
KeAcquireSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(Ext2GlobalData.IrpContextZoneHeader), PtrIrpContext);
|
|
KeReleaseSpinLock(&(Ext2GlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
}
|
|
else
|
|
{
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", PtrIrpContext);
|
|
ExFreePool(PtrIrpContext);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2PostRequest()
|
|
*
|
|
* 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 NTAPI Ext2PostRequest(
|
|
PtrExt2IrpContext PtrIrpContext,
|
|
PIRP PtrIrp)
|
|
{
|
|
NTSTATUS RC = STATUS_PENDING;
|
|
|
|
DebugTrace(DEBUG_TRACE_ASYNC, " === Asynchronous request. Deferring processing", 0);
|
|
|
|
// mark the IRP pending
|
|
IoMarkIrpPending(PtrIrp);
|
|
|
|
// queue up the request
|
|
ExInterlockedInsertTailList(
|
|
&Ext2GlobalData.ThreadQueue.ThreadQueueListHead,
|
|
&PtrIrpContext->ThreadQueueListEntry,
|
|
&Ext2GlobalData.ThreadQueue.SpinLock );
|
|
|
|
KeSetEvent( &Ext2GlobalData.ThreadQueue.QueueEvent, 0, FALSE );
|
|
|
|
|
|
/***************** not using system worker threads *****************
|
|
ExInitializeWorkItem(&(PtrIrpContext->WorkQueueItem), Ext2CommonDispatch, PtrIrpContext);
|
|
ExQueueWorkItem( &( PtrIrpContext->WorkQueueItem ), DelayedWorkQueue );
|
|
// CriticalWorkQueue
|
|
*****************************************************************************/
|
|
|
|
// return status pending
|
|
return(RC);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2CommonDispatch()
|
|
*
|
|
* 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 NTAPI Ext2CommonDispatch(
|
|
void *Context ) // actually an IRPContext structure
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrExt2IrpContext PtrIrpContext = NULL;
|
|
PIRP PtrIrp = NULL;
|
|
|
|
// The context must be a pointer to an IrpContext structure
|
|
PtrIrpContext = (PtrExt2IrpContext)Context;
|
|
ASSERT(PtrIrpContext);
|
|
|
|
// Assert that the Context is legitimate
|
|
if ((PtrIrpContext->NodeIdentifier.NodeType != EXT2_NODE_TYPE_IRP_CONTEXT) || (PtrIrpContext->NodeIdentifier.NodeSize != Ext2QuadAlign(sizeof(Ext2IrpContext))))
|
|
{
|
|
// This does not look good
|
|
Ext2Panic(EXT2_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 & EXT2_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
|
|
Ext2SetFlag(PtrIrpContext->IrpContextFlags, EXT2_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
|
|
DebugTrace(DEBUG_TRACE_ASYNC, " === Serviceing IRP_MJ_CREATE request asynchronously .", 0);
|
|
(void)Ext2CommonCreate(PtrIrpContext, PtrIrp, FALSE);
|
|
break;
|
|
case IRP_MJ_READ:
|
|
// Invoke the common read routine
|
|
DebugTrace(DEBUG_TRACE_ASYNC, " === Serviceing IRP_MJ_READ request asynchronously .", 0);
|
|
(void)Ext2CommonRead(PtrIrpContext, PtrIrp, FALSE);
|
|
break;
|
|
case IRP_MJ_WRITE:
|
|
// Invoke the common write routine
|
|
DebugTrace(DEBUG_TRACE_ASYNC, " === Serviceing IRP_MJ_WRITE request asynchronously .", 0);
|
|
(void)Ext2CommonWrite(PtrIrpContext, PtrIrp );
|
|
break;
|
|
|
|
case IRP_MJ_CLEANUP:
|
|
// Invoke the common read routine
|
|
DebugTrace(DEBUG_TRACE_ASYNC, " === Serviceing IRP_MJ_CLEANUP request asynchronously .", 0);
|
|
(void)Ext2CommonCleanup(PtrIrpContext, PtrIrp, FALSE);
|
|
break;
|
|
case IRP_MJ_CLOSE:
|
|
// Invoke the common read routine
|
|
DebugTrace(DEBUG_TRACE_ASYNC, " === Serviceing IRP_MJ_CLOSE request asynchronously .", 0);
|
|
(void)Ext2CommonClose ( PtrIrpContext, PtrIrp, FALSE );
|
|
break;
|
|
|
|
// Continue with the remaining possible dispatch routines below ...
|
|
default:
|
|
// This is the case where we have an invalid major function
|
|
DebugTrace(DEBUG_TRACE_ASYNC, " === Serviceing asynchronous request. \nUnable to recoganise the IRP!!! How can this be!!!", 0);
|
|
PtrIrp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
PtrIrp->IoStatus.Information = 0;
|
|
|
|
Ext2BreakPoint();
|
|
|
|
IoCompleteRequest(PtrIrp, IO_NO_INCREMENT);
|
|
break;
|
|
}
|
|
}
|
|
except (Ext2ExceptionFilter(PtrIrpContext, GetExceptionInformation()))
|
|
{
|
|
RC = Ext2ExceptionHandler(PtrIrpContext, PtrIrp);
|
|
Ext2LogEvent(EXT2_ERROR_INTERNAL_ERROR, RC);
|
|
}
|
|
|
|
// Enable preemption
|
|
FsRtlExitFileSystem();
|
|
|
|
// Ensure that the "top-level" field is cleared
|
|
IoSetTopLevelIrp(NULL);
|
|
|
|
PsTerminateSystemThread( RC );
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2InitializeVCB()
|
|
*
|
|
* Description:
|
|
* Perform the initialization for a VCB structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
void NTAPI Ext2InitializeVCB(
|
|
PDEVICE_OBJECT PtrVolumeDeviceObject,
|
|
PDEVICE_OBJECT PtrTargetDeviceObject,
|
|
PVPB PtrVPB,
|
|
PLARGE_INTEGER AllocationSize )
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrExt2VCB PtrVCB = NULL;
|
|
BOOLEAN VCBResourceInitialized = FALSE;
|
|
|
|
PtrVCB = (PtrExt2VCB)(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(Ext2VCB));
|
|
|
|
// Initialize the signature fields
|
|
PtrVCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_VCB;
|
|
PtrVCB->NodeIdentifier.NodeSize = sizeof(Ext2VCB);
|
|
|
|
// Initialize the ERESOURCE objects.
|
|
RC = ExInitializeResourceLite(&(PtrVCB->VCBResource));
|
|
RC = ExInitializeResourceLite(&(PtrVCB->PagingIoResource));
|
|
|
|
ASSERT(NT_SUCCESS(RC));
|
|
VCBResourceInitialized = TRUE;
|
|
|
|
PtrVCB->TargetDeviceObject = PtrTargetDeviceObject;
|
|
|
|
PtrVCB->VCBDeviceObject = PtrVolumeDeviceObject;
|
|
|
|
PtrVCB->PtrVPB = PtrVPB;
|
|
|
|
// Initialize the list anchor (head) for some lists in this VCB.
|
|
InitializeListHead(&(PtrVCB->FCBListHead));
|
|
InitializeListHead(&(PtrVCB->NextNotifyIRP));
|
|
InitializeListHead(&(PtrVCB->VolumeOpenListHead));
|
|
InitializeListHead(&(PtrVCB->ClosableFCBs.ClosableFCBListHead));
|
|
PtrVCB->ClosableFCBs.Count = 0;
|
|
|
|
// 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->CommonVCBHeader.AllocationSize.QuadPart = AllocationSize->QuadPart;
|
|
|
|
PtrVCB->CommonVCBHeader.FileSize.QuadPart = AllocationSize->QuadPart;
|
|
// 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->CommonVCBHeader.ValidDataLength.LowPart = 0xFFFFFFFF;
|
|
PtrVCB->CommonVCBHeader.ValidDataLength.HighPart = 0x7FFFFFFF;
|
|
|
|
PtrVCB->CommonVCBHeader.IsFastIoPossible = FastIoIsNotPossible;
|
|
|
|
PtrVCB->CommonVCBHeader.Resource = &(PtrVCB->VCBResource);
|
|
PtrVCB->CommonVCBHeader.PagingIoResource = &(PtrVCB->PagingIoResource);
|
|
|
|
// 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->CommonVCBHeader);
|
|
PtrVCB->PtrStreamFileObject->FsContext2 = NULL;
|
|
PtrVCB->PtrStreamFileObject->SectionObjectPointer = &(PtrVCB->SectionObject);
|
|
|
|
PtrVCB->PtrStreamFileObject->Vpb = PtrVPB;
|
|
PtrVCB->PtrStreamFileObject->ReadAccess = TRUE;
|
|
PtrVCB->PtrStreamFileObject->WriteAccess = TRUE;
|
|
|
|
// Link this chap onto the global linked list of all VCB structures.
|
|
DebugTrace(DEBUG_TRACE_MISC, "*** Attempting to acquire Global Resource Exclusively [FileInfo]", 0);
|
|
ExAcquireResourceExclusiveLite(&(Ext2GlobalData.GlobalDataResource), TRUE);
|
|
InsertTailList(&(Ext2GlobalData.NextVCB), &(PtrVCB->NextVCB));
|
|
DebugTrace(DEBUG_TRACE_MISC, "*** Global Resource Acquired [FileInfo]", 0);
|
|
|
|
|
|
|
|
// Initialize caching for the stream file object.
|
|
CcInitializeCacheMap(PtrVCB->PtrStreamFileObject, (PCC_FILE_SIZES)(&(PtrVCB->CommonVCBHeader.AllocationSize)),
|
|
TRUE, // We will use pinned access.
|
|
&(Ext2GlobalData.CacheMgrCallBacks), PtrVCB );
|
|
|
|
|
|
Ext2ReleaseResource(&(Ext2GlobalData.GlobalDataResource));
|
|
DebugTrace(DEBUG_TRACE_MISC, "*** Global Resource Released[FileInfo]", 0);
|
|
|
|
// Mark the fact that this VCB structure is initialized.
|
|
Ext2SetFlag(PtrVCB->VCBFlags, EXT2_VCB_FLAGS_VCB_INITIALIZED);
|
|
PtrVCB->PtrGroupDescriptors = NULL;
|
|
PtrVCB->NoOfGroups = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2CompleteRequest()
|
|
*
|
|
* Description:
|
|
* This routine completes a Irp.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* ???
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Irp - Supplies the Irp being processed
|
|
*
|
|
* Status - Supplies the status to complete the Irp with
|
|
*
|
|
* Return Value: none
|
|
*
|
|
*************************************************************************/
|
|
void NTAPI Ext2CompleteRequest(
|
|
IN PIRP Irp OPTIONAL,
|
|
IN NTSTATUS Status
|
|
)
|
|
{
|
|
//
|
|
// If we have an Irp then complete the irp.
|
|
//
|
|
|
|
if (Irp != NULL)
|
|
{
|
|
|
|
//
|
|
// We got an error, so zero out the information field before
|
|
// completing the request if this was an input operation.
|
|
// Otherwise IopCompleteRequest will try to copy to the user's buffer.
|
|
//
|
|
|
|
if ( NT_ERROR(Status) &&
|
|
FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2CreateNewCCB()
|
|
*
|
|
* Description:
|
|
* We want to create a new CCB.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: A pointer to the CCB structure OR NULL.
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS NTAPI Ext2CreateNewCCB(
|
|
PtrExt2CCB *ReturnedCCB,
|
|
PtrExt2FCB PtrFCB,
|
|
PFILE_OBJECT PtrFileObject )
|
|
{
|
|
PtrExt2CCB PtrCCB;
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
|
|
try
|
|
{
|
|
|
|
PtrCCB = Ext2AllocateCCB();
|
|
if (!PtrFCB)
|
|
{
|
|
// Assume lack of memory.
|
|
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
PtrCCB->PtrFCB = PtrFCB;
|
|
|
|
PtrCCB->PtrFileObject = PtrFileObject;
|
|
PtrCCB->CurrentByteOffset.QuadPart = 0;
|
|
|
|
if( PtrFCB->ClosableFCBs.OnClosableFCBList )
|
|
{
|
|
// This FCB was on the Closable List...
|
|
// Taking it off the list...
|
|
//
|
|
RemoveEntryList( &PtrFCB->ClosableFCBs.ClosableFCBList );
|
|
PtrFCB->ClosableFCBs.OnClosableFCBList = FALSE;
|
|
PtrFCB->PtrVCB->ClosableFCBs.Count --;
|
|
}
|
|
|
|
InterlockedIncrement( &PtrFCB->ReferenceCount );
|
|
InterlockedIncrement( &PtrFCB->OpenHandleCount );
|
|
|
|
InsertTailList( &( PtrFCB->CCBListHead ), &(PtrCCB->NextCCB));
|
|
|
|
*ReturnedCCB = PtrCCB;
|
|
try_exit: NOTHING;
|
|
}
|
|
finally
|
|
{
|
|
|
|
}
|
|
|
|
return(RC);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2DenyAccess()
|
|
*
|
|
* Description:
|
|
* We want to deny access to an IRP
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: NTSTATUS - STATUS_ACCESS_DENIED (always)
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS NTAPI Ext2DenyAccess( IN PIRP Irp )
|
|
{
|
|
ASSERT( Irp );
|
|
|
|
// Just return Access Denied
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|
|
|
DebugTrace(DEBUG_TRACE_MISC, "DENYING ACCESS (this will do for now!)...", 0);
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: Ext2GetFCB_CCB_VCB_FromFileObject()
|
|
*
|
|
* Description:
|
|
* This routine retrieves the FCB, CCB and VCB from the File Object...
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* ?
|
|
*
|
|
* Return Value: NTSTATUS - STATUS_SUCCESS(always)
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS NTAPI Ext2GetFCB_CCB_VCB_FromFileObject (
|
|
IN PFILE_OBJECT PtrFileObject,
|
|
OUT PtrExt2FCB *PPtrFCB,
|
|
OUT PtrExt2CCB *PPtrCCB,
|
|
OUT PtrExt2VCB *PPtrVCB )
|
|
{
|
|
(*PPtrCCB) = (PtrExt2CCB)(PtrFileObject->FsContext2);
|
|
if( *PPtrCCB )
|
|
{
|
|
ASSERT((*PPtrCCB)->NodeIdentifier.NodeType == EXT2_NODE_TYPE_CCB);
|
|
(*PPtrFCB) = (*PPtrCCB)->PtrFCB;
|
|
|
|
ASSERT((*PPtrFCB));
|
|
|
|
if ((*PPtrFCB)->NodeIdentifier.NodeType == EXT2_NODE_TYPE_VCB)
|
|
{
|
|
(*PPtrVCB) = (PtrExt2VCB)(*PPtrFCB);
|
|
AssertVCB( (*PPtrVCB) );
|
|
|
|
// No FCB
|
|
(*PPtrFCB) = NULL;
|
|
//found a VCB
|
|
}
|
|
else
|
|
{
|
|
AssertFCB( (*PPtrFCB) );
|
|
(*PPtrVCB) = (*PPtrFCB)->PtrVCB;
|
|
AssertVCB( (*PPtrVCB) );
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// PtrFileObject->FsContext points to NTRequiredFCB
|
|
(*PPtrFCB) = CONTAINING_RECORD( PtrFileObject->FsContext, Ext2FCB, NTRequiredFCB );
|
|
ASSERT((*PPtrFCB));
|
|
//(*PPtrFCB) = PtrFileObject->FsContext;
|
|
|
|
if ((*PPtrFCB)->NodeIdentifier.NodeType == EXT2_NODE_TYPE_FCB)
|
|
{
|
|
// Making sure I got it right...
|
|
AssertFCB( *PPtrFCB );
|
|
(*PPtrVCB) = (*PPtrFCB)->PtrVCB;
|
|
AssertVCB( *PPtrVCB );
|
|
}
|
|
else
|
|
{
|
|
// This should be a VCB
|
|
|
|
(*PPtrVCB) = CONTAINING_RECORD( PtrFileObject->FsContext, Ext2VCB, CommonVCBHeader );
|
|
AssertVCB( *PPtrVCB );
|
|
|
|
// No FCB
|
|
(*PPtrFCB) = NULL;
|
|
//found a VCB
|
|
}
|
|
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
void NTAPI Ext2CopyUnicodeString( PUNICODE_STRING PtrDestinationString, PUNICODE_STRING PtrSourceString )
|
|
{
|
|
int Count;
|
|
// Allcating space for Destination...
|
|
PtrDestinationString->Length = PtrSourceString->Length;
|
|
PtrDestinationString->MaximumLength = Ext2QuadAlign( PtrSourceString->Length + 2 );
|
|
PtrDestinationString->Buffer = Ext2AllocatePool( NonPagedPool, PtrDestinationString->MaximumLength );
|
|
|
|
// RtlCopyUnicodeString( PtrDestinationString, PtrSourceString );
|
|
|
|
for( Count = 0 ; Count < (PtrSourceString->Length/2) ; Count++ )
|
|
{
|
|
PtrDestinationString->Buffer[Count] = PtrSourceString->Buffer[Count];
|
|
}
|
|
PtrDestinationString->Buffer[Count] = 0;
|
|
|
|
}
|
|
|
|
void NTAPI Ext2CopyWideCharToUnicodeString(
|
|
PUNICODE_STRING PtrDestinationString,
|
|
PCWSTR PtrSourceString )
|
|
{
|
|
|
|
int Count;
|
|
|
|
// Determining length...
|
|
for( Count = 0 ; PtrSourceString[Count] != 0 ; Count++ );
|
|
|
|
// Allcating space for Destination...
|
|
PtrDestinationString->Length = Count * 2;
|
|
PtrDestinationString->MaximumLength = Ext2QuadAlign( Count * 2 + 2 );
|
|
PtrDestinationString->Buffer = Ext2AllocatePool( NonPagedPool, PtrDestinationString->MaximumLength );
|
|
|
|
// Copying the string over...
|
|
for( Count = 0 ; ; Count++ )
|
|
{
|
|
PtrDestinationString->Buffer[Count] = PtrSourceString[Count];
|
|
if( PtrSourceString[Count] == 0 )
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void NTAPI Ext2CopyCharToUnicodeString(
|
|
PUNICODE_STRING PtrDestinationString,
|
|
PCSTR PtrSourceString,
|
|
USHORT SourceStringLength )
|
|
{
|
|
int Count;
|
|
// Allcating space for Destination...
|
|
PtrDestinationString->Length = SourceStringLength * 2;
|
|
PtrDestinationString->MaximumLength = Ext2QuadAlign( SourceStringLength * 2 + 2 );
|
|
PtrDestinationString->Buffer = Ext2AllocatePool( NonPagedPool, PtrDestinationString->MaximumLength );
|
|
|
|
// Copying the string over...
|
|
for( Count = 0 ; Count < SourceStringLength ; Count++ )
|
|
{
|
|
PtrDestinationString->Buffer[Count] = PtrSourceString[Count];
|
|
}
|
|
PtrDestinationString->Buffer[Count] = 0;
|
|
|
|
}
|
|
|
|
void NTAPI Ext2CopyZCharToUnicodeString( PUNICODE_STRING PtrDestinationString, PCSTR PtrSourceString )
|
|
{
|
|
|
|
int Count;
|
|
|
|
// Determining length...
|
|
for( Count = 0 ; PtrSourceString[Count] != 0 ; Count++ );
|
|
|
|
// Allcating space for Destination...
|
|
PtrDestinationString->Length = Count * 2;
|
|
PtrDestinationString->MaximumLength = Ext2QuadAlign( Count * 2 + 2 );
|
|
PtrDestinationString->Buffer = Ext2AllocatePool( NonPagedPool, PtrDestinationString->MaximumLength );
|
|
|
|
// Copying the string over...
|
|
for( Count = 0 ; ; Count++ )
|
|
{
|
|
PtrDestinationString->Buffer[Count] = PtrSourceString[Count];
|
|
if( PtrSourceString[Count] == 0 )
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NTAPI Ext2ZerooutUnicodeString( PUNICODE_STRING PtrUnicodeString )
|
|
{
|
|
PtrUnicodeString->Length = 0;
|
|
PtrUnicodeString->MaximumLength =0;
|
|
PtrUnicodeString->Buffer = 0;
|
|
}
|
|
|
|
void NTAPI Ext2DeallocateUnicodeString( PUNICODE_STRING PtrUnicodeString )
|
|
{
|
|
if( PtrUnicodeString && PtrUnicodeString->Buffer )
|
|
{
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", PtrUnicodeString->Buffer );
|
|
ExFreePool( PtrUnicodeString->Buffer );
|
|
}
|
|
PtrUnicodeString->Length = 0;
|
|
PtrUnicodeString->MaximumLength =0;
|
|
PtrUnicodeString->Buffer = 0;
|
|
}
|
|
|
|
PtrExt2FCB NTAPI Ext2GetUsedFCB(
|
|
PtrExt2VCB PtrVCB )
|
|
{
|
|
|
|
BOOLEAN AllocatedFromZone = FALSE;
|
|
PLIST_ENTRY PtrEntry = NULL;
|
|
PtrExt2FCB PtrFCB = NULL;
|
|
|
|
ASSERT( PtrVCB );
|
|
if( PtrVCB->ClosableFCBs.Count < EXT2_MAXCLOSABLE_FCBS_LL )
|
|
{
|
|
//
|
|
// Too few Closable FCBs
|
|
// Will not reuse any FCBs
|
|
// Allocating a new one
|
|
//
|
|
return Ext2AllocateFCB();
|
|
}
|
|
//
|
|
// Obtaining a used FCB...
|
|
//
|
|
|
|
// Retrieving the first entry in the closable FCB list...
|
|
|
|
PtrEntry = RemoveHeadList( &PtrVCB->ClosableFCBs.ClosableFCBListHead );
|
|
|
|
PtrFCB = CONTAINING_RECORD( PtrEntry, Ext2FCB, ClosableFCBs.ClosableFCBList );
|
|
|
|
// Remembering if the FCB was allocated from the Zone...
|
|
AllocatedFromZone = Ext2IsFlagOn( PtrFCB->FCBFlags, EXT2_FCB_NOT_FROM_ZONE );
|
|
|
|
//
|
|
// Close this FCB
|
|
//
|
|
if( !Ext2CloseClosableFCB( PtrFCB ) )
|
|
{
|
|
// Couldn't close the FCB!!
|
|
//
|
|
InsertHeadList( &PtrVCB->ClosableFCBs.ClosableFCBListHead,
|
|
&PtrFCB->ClosableFCBs.ClosableFCBList );
|
|
return Ext2AllocateFCB();
|
|
}
|
|
|
|
PtrVCB->ClosableFCBs.Count--;
|
|
DebugTrace( DEBUG_TRACE_SPECIAL, "Count = %ld [Ext2GetUsedFCB]", PtrVCB->ClosableFCBs.Count );
|
|
|
|
//
|
|
// Getting the FCB ready for reuse by
|
|
// zeroing it out...
|
|
//
|
|
RtlZeroMemory(PtrFCB, Ext2QuadAlign(sizeof(Ext2FCB)));
|
|
|
|
// set up some fields ...
|
|
PtrFCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_FCB;
|
|
PtrFCB->NodeIdentifier.NodeSize = Ext2QuadAlign(sizeof(Ext2FCB));
|
|
|
|
|
|
if (!AllocatedFromZone)
|
|
{
|
|
Ext2SetFlag(PtrFCB->FCBFlags, EXT2_FCB_NOT_FROM_ZONE);
|
|
}
|
|
|
|
return PtrFCB;
|
|
}
|
|
|
|
BOOLEAN NTAPI Ext2CloseClosableFCB(
|
|
PtrExt2FCB PtrFCB)
|
|
{
|
|
KIRQL Irql = 0;
|
|
PFILE_OBJECT PtrFileObject = NULL;
|
|
|
|
AssertFCB( PtrFCB );
|
|
|
|
// Attempting to acquire the FCB Exclusively...
|
|
if(! ExAcquireResourceExclusiveLite( &(PtrFCB->NTRequiredFCB.MainResource ), FALSE ) )
|
|
{
|
|
Ext2BreakPoint();
|
|
return FALSE;
|
|
}
|
|
|
|
Irql = KeGetCurrentIrql( );
|
|
|
|
if( PtrFCB->ReferenceCount )
|
|
{
|
|
// How the hell can this happen!!!
|
|
Ext2BreakPoint();
|
|
}
|
|
if( PtrFCB->OpenHandleCount )
|
|
{
|
|
// How the hell can this happen!!!
|
|
Ext2BreakPoint();
|
|
}
|
|
|
|
// Deleting entry from VCB's FCB list...
|
|
RemoveEntryList( &PtrFCB->NextFCB );
|
|
|
|
PtrFCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_FREED;
|
|
|
|
PtrFileObject = PtrFCB->DcbFcb.Dcb.PtrDirFileObject;
|
|
|
|
if ( PtrFileObject )
|
|
{
|
|
//
|
|
// Clear the Cache Map...
|
|
//
|
|
if( PtrFileObject->PrivateCacheMap != NULL)
|
|
{
|
|
IO_STATUS_BLOCK Status;
|
|
DebugTrace( DEBUG_TRACE_SPECIAL, ">>.........Flushing cache.........<<", 0 );
|
|
CcFlushCache( PtrFileObject->SectionObjectPointer, NULL, 0, &Status );
|
|
CcUninitializeCacheMap( PtrFileObject, NULL, NULL );
|
|
}
|
|
//
|
|
// The File Object is no longer required...
|
|
// Close it by dereferenceing it!!!
|
|
//
|
|
PtrFileObject->FsContext = NULL;
|
|
PtrFileObject->FsContext2 = NULL;
|
|
ObDereferenceObject( PtrFileObject );
|
|
|
|
PtrFCB->DcbFcb.Dcb.PtrDirFileObject = NULL;
|
|
PtrFileObject = NULL;
|
|
}
|
|
|
|
// Uninitialize the Resources...
|
|
ExDeleteResourceLite( &PtrFCB->NTRequiredFCB.MainResource );
|
|
ExDeleteResourceLite( &PtrFCB->NTRequiredFCB.PagingIoResource );
|
|
|
|
//
|
|
// Releasing the FCB Name Object...
|
|
//
|
|
if( PtrFCB->FCBName )
|
|
{
|
|
DebugTrace( DEBUG_TRACE_SPECIAL, "Reusing FCB - File Name %S", PtrFCB->FCBName->ObjectName.Buffer );
|
|
Ext2ReleaseObjectName( PtrFCB->FCBName );
|
|
}
|
|
else
|
|
{
|
|
DebugTrace( DEBUG_TRACE_SPECIAL, "Reusing FCB - File Name *Unknown*", 0 );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN NTAPI Ext2SaveBCB(
|
|
PtrExt2IrpContext PtrIrpContext,
|
|
PBCB PtrBCB,
|
|
PFILE_OBJECT PtrFileObject)
|
|
{
|
|
PEXT2_SAVED_BCBS PtrSavedBCB;
|
|
PLIST_ENTRY PtrEntry = NULL;
|
|
|
|
if( !PtrIrpContext )
|
|
{
|
|
//
|
|
// NULL passed instead of the IRP Context
|
|
// This call should be ignored...
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
if( !AssertBCB( PtrBCB ) )
|
|
{
|
|
DebugTrace( DEBUG_TRACE_MISC, "Not saving BCB!!! [Ext2SaveBCB]", 0 );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DebugTrace( DEBUG_TRACE_SPECIAL, "Saving BCB [Ext2SaveBCB]", 0 );
|
|
|
|
// Has the BCB been saved already?
|
|
for( PtrEntry = PtrIrpContext->SavedBCBsListHead.Flink;
|
|
PtrEntry != &PtrIrpContext->SavedBCBsListHead;
|
|
PtrEntry = PtrEntry->Flink )
|
|
{
|
|
PtrSavedBCB = CONTAINING_RECORD( PtrEntry, EXT2_SAVED_BCBS, SavedBCBsListEntry );
|
|
ASSERT( PtrSavedBCB );
|
|
if( PtrSavedBCB->PtrBCB == PtrBCB )
|
|
{
|
|
|
|
// A BCB for this file has already been saved for flushing...
|
|
// Won't resave this one...
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// Reference the BCB
|
|
CcRepinBcb( PtrBCB );
|
|
|
|
// Now allocate a EXT2_SAVED_BCBS
|
|
PtrSavedBCB = Ext2AllocatePool( NonPagedPool,
|
|
Ext2QuadAlign( sizeof( EXT2_SAVED_BCBS ) ) );
|
|
if( !PtrSavedBCB )
|
|
return FALSE;
|
|
PtrSavedBCB->NodeIdentifier.NodeSize = sizeof( EXT2_SAVED_BCBS );
|
|
PtrSavedBCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_SAVED_BCB;
|
|
|
|
PtrSavedBCB->PtrBCB = PtrBCB;
|
|
// PtrSavedBCB->PtrFileObject = PtrFileObject;
|
|
|
|
// Now save it in the IRP Context
|
|
InsertHeadList( &PtrIrpContext->SavedBCBsListHead, &PtrSavedBCB->SavedBCBsListEntry );
|
|
|
|
PtrIrpContext->SavedCount++;
|
|
// Return success...
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN NTAPI Ext2FlushSavedBCBs(
|
|
PtrExt2IrpContext PtrIrpContext )
|
|
{
|
|
|
|
PLIST_ENTRY PtrEntry = NULL;
|
|
PEXT2_SAVED_BCBS PtrSavedBCB = NULL;
|
|
IO_STATUS_BLOCK Status;
|
|
BOOLEAN RC = TRUE;
|
|
|
|
if( !IsListEmpty( &PtrIrpContext->SavedBCBsListHead ) )
|
|
{
|
|
DebugTrace( DEBUG_TRACE_SPECIAL, "Flushing cache... - Ext2FlushSavedBCBs", 0 );
|
|
}
|
|
while( !IsListEmpty( &PtrIrpContext->SavedBCBsListHead ) )
|
|
{
|
|
|
|
PtrEntry = RemoveTailList( &PtrIrpContext->SavedBCBsListHead );
|
|
if( !PtrEntry )
|
|
{
|
|
// No more entries left...
|
|
break;
|
|
}
|
|
|
|
// Get the Saved BCB
|
|
PtrSavedBCB = CONTAINING_RECORD( PtrEntry, EXT2_SAVED_BCBS, SavedBCBsListEntry );
|
|
if( PtrSavedBCB->NodeIdentifier.NodeType != EXT2_NODE_TYPE_SAVED_BCB )
|
|
{
|
|
// Something is wrong...
|
|
Ext2BreakPoint();
|
|
return FALSE;
|
|
}
|
|
|
|
if( !AssertBCB( PtrSavedBCB->PtrBCB ) )
|
|
{
|
|
// This BCB shouldn't have been saved in the first place...
|
|
DebugTrace( DEBUG_TRACE_ERROR, "Unable to flush BCB - Skipping!!! [Ext2SaveBCB]", 0 );
|
|
continue;
|
|
}
|
|
|
|
// Unpin and Flush the cache...
|
|
CcUnpinRepinnedBcb( PtrSavedBCB->PtrBCB, TRUE, &Status );
|
|
|
|
if( !NT_SUCCESS( Status.Status ) )
|
|
{
|
|
// Failure in flushing...
|
|
DebugTrace( DEBUG_TRACE_SPECIAL, "Failure flushing cache - Ext2FlushSavedBCBs", 0 );
|
|
RC = FALSE;
|
|
}
|
|
|
|
// Release the Saved BCB
|
|
PtrSavedBCB->NodeIdentifier.NodeType = EXT2_NODE_TYPE_INVALID;
|
|
|
|
DebugTrace( DEBUG_TRACE_FREE, "Freeing = %lX [misc]", PtrSavedBCB );
|
|
ExFreePool( PtrSavedBCB );
|
|
PtrSavedBCB = NULL;
|
|
PtrIrpContext->SavedCount--;
|
|
}
|
|
|
|
return RC;
|
|
}
|
|
|
|
BOOLEAN NTAPI AssertBCB( PBCB PtrBCB )
|
|
{
|
|
PFILE_OBJECT PtrFileObject = NULL;
|
|
|
|
/*
|
|
* This routine is simplified version of the original
|
|
* AssertBCB and doesn't make any assumptions about
|
|
* the layout of undocumented BCB structure.
|
|
* -- Filip Navara, 18/08/2004
|
|
*/
|
|
|
|
PtrFileObject = CcGetFileObjectFromBcb ( PtrBCB );
|
|
if( !PtrFileObject )
|
|
{
|
|
Ext2BreakPoint();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
ULONG NTAPI Ext2Align( ULONG NumberToBeAligned, ULONG Alignment )
|
|
{
|
|
if( Alignment & ( Alignment - 1 ) )
|
|
{
|
|
//
|
|
// Alignment not a power of 2
|
|
// Just returning
|
|
//
|
|
return NumberToBeAligned;
|
|
}
|
|
if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
|
|
{
|
|
NumberToBeAligned = NumberToBeAligned + Alignment;
|
|
NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
|
|
}
|
|
return NumberToBeAligned;
|
|
}
|
|
|
|
LONGLONG NTAPI Ext2Align64( LONGLONG NumberToBeAligned, LONGLONG Alignment )
|
|
{
|
|
if( Alignment & ( Alignment - 1 ) )
|
|
{
|
|
//
|
|
// Alignment not a power of 2
|
|
// Just returning
|
|
//
|
|
return NumberToBeAligned;
|
|
}
|
|
if( ( NumberToBeAligned & ( Alignment - 1 ) ) != 0 )
|
|
{
|
|
NumberToBeAligned = NumberToBeAligned + Alignment;
|
|
NumberToBeAligned = NumberToBeAligned & ( ~ (Alignment-1) );
|
|
}
|
|
return NumberToBeAligned;
|
|
}
|
|
|
|
|
|
ULONG NTAPI Ext2GetCurrentTime()
|
|
{
|
|
LARGE_INTEGER CurrentTime;
|
|
ULONG Time;
|
|
KeQuerySystemTime( &CurrentTime );
|
|
Time = (ULONG) ( (CurrentTime.QuadPart - Ext2GlobalData.TimeDiff.QuadPart) / 10000000 );
|
|
return Time;
|
|
}
|