mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 07:08:19 +00:00
2595 lines
88 KiB
C++
2595 lines
88 KiB
C++
////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
|
|
// All rights reserved
|
|
// This file was released under the GPLv2 on June 2015.
|
|
////////////////////////////////////////////////////////////////////
|
|
/*
|
|
|
|
File: Misc.cpp
|
|
|
|
Module: UDF File System Driver (Kernel mode execution only)
|
|
|
|
Description:
|
|
This file contains some miscellaneous support routines.
|
|
|
|
*/
|
|
|
|
#include "udffs.h"
|
|
// define the file specific bug-check id
|
|
#define UDF_BUG_CHECK_ID UDF_FILE_MISC
|
|
|
|
#include <stdio.h>
|
|
|
|
//CCHAR DefLetter[] = {""};
|
|
|
|
/*
|
|
|
|
Function: UDFInitializeZones()
|
|
|
|
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
|
|
UDFInitializeZones(VOID)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
uint32 SizeOfZone = UDFGlobalData.DefaultZoneSizeInNumStructs;
|
|
uint32 SizeOfObjectNameZone = 0;
|
|
uint32 SizeOfCCBZone = 0;
|
|
// uint32 SizeOfFCBZone = 0;
|
|
uint32 SizeOfIrpContextZone = 0;
|
|
// uint32 SizeOfFileInfoZone = 0;
|
|
|
|
_SEH2_TRY {
|
|
|
|
// initialize the spinlock protecting the zones
|
|
KeInitializeSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock));
|
|
|
|
// determine memory requirements
|
|
switch (MmQuerySystemSize()) {
|
|
case MmMediumSystem:
|
|
SizeOfObjectNameZone = (4 * SizeOfZone * UDFQuadAlign(sizeof(UDFObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfCCBZone = (4 * SizeOfZone * UDFQuadAlign(sizeof(UDFCCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfIrpContextZone = (4 * SizeOfZone * UDFQuadAlign(sizeof(UDFIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
UDFGlobalData.MaxDelayedCloseCount = 24;
|
|
UDFGlobalData.MinDelayedCloseCount = 6;
|
|
UDFGlobalData.MaxDirDelayedCloseCount = 8;
|
|
UDFGlobalData.MinDirDelayedCloseCount = 2;
|
|
UDFGlobalData.WCacheMaxFrames = 8*4;
|
|
UDFGlobalData.WCacheMaxBlocks = 16*64;
|
|
UDFGlobalData.WCacheBlocksPerFrameSh = 8;
|
|
UDFGlobalData.WCacheFramesToKeepFree = 4;
|
|
break;
|
|
case MmLargeSystem:
|
|
SizeOfObjectNameZone = (8 * SizeOfZone * UDFQuadAlign(sizeof(UDFObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfCCBZone = (8 * SizeOfZone * UDFQuadAlign(sizeof(UDFCCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfIrpContextZone = (8 * SizeOfZone * UDFQuadAlign(sizeof(UDFIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
UDFGlobalData.MaxDelayedCloseCount = 72;
|
|
UDFGlobalData.MinDelayedCloseCount = 18;
|
|
UDFGlobalData.MaxDirDelayedCloseCount = 24;
|
|
UDFGlobalData.MinDirDelayedCloseCount = 6;
|
|
UDFGlobalData.WCacheMaxFrames = 2*16*4;
|
|
UDFGlobalData.WCacheMaxBlocks = 2*16*64;
|
|
UDFGlobalData.WCacheBlocksPerFrameSh = 8;
|
|
UDFGlobalData.WCacheFramesToKeepFree = 8;
|
|
break;
|
|
case MmSmallSystem:
|
|
default:
|
|
SizeOfObjectNameZone = (2 * SizeOfZone * UDFQuadAlign(sizeof(UDFObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfCCBZone = (2 * SizeOfZone * UDFQuadAlign(sizeof(UDFCCB))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
SizeOfIrpContextZone = (2 * SizeOfZone * UDFQuadAlign(sizeof(UDFIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
|
|
UDFGlobalData.MaxDelayedCloseCount = 8;
|
|
UDFGlobalData.MinDelayedCloseCount = 2;
|
|
UDFGlobalData.MaxDirDelayedCloseCount = 6;
|
|
UDFGlobalData.MinDirDelayedCloseCount = 1;
|
|
UDFGlobalData.WCacheMaxFrames = 8*4/2;
|
|
UDFGlobalData.WCacheMaxBlocks = 16*64/2;
|
|
UDFGlobalData.WCacheBlocksPerFrameSh = 8;
|
|
UDFGlobalData.WCacheFramesToKeepFree = 2;
|
|
}
|
|
|
|
// typical NT methodology (at least until *someone* exposed the "difference" between a server and workstation ;-)
|
|
if (MmIsThisAnNtAsSystem()) {
|
|
SizeOfObjectNameZone *= UDF_NTAS_MULTIPLE;
|
|
SizeOfCCBZone *= UDF_NTAS_MULTIPLE;
|
|
SizeOfIrpContextZone *= UDF_NTAS_MULTIPLE;
|
|
}
|
|
|
|
// allocate memory for each of the zones and initialize the zones ...
|
|
if (!(UDFGlobalData.ObjectNameZone = DbgAllocatePool(NonPagedPool, SizeOfObjectNameZone))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return(RC);
|
|
}
|
|
|
|
if (!(UDFGlobalData.CCBZone = DbgAllocatePool(NonPagedPool, SizeOfCCBZone))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return(RC);
|
|
}
|
|
|
|
if (!(UDFGlobalData.IrpContextZone = DbgAllocatePool(NonPagedPool, SizeOfIrpContextZone))) {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return(RC);
|
|
}
|
|
|
|
// initialize each of the zone headers ...
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(UDFGlobalData.ObjectNameZoneHeader),
|
|
UDFQuadAlign(sizeof(UDFObjectName)),
|
|
UDFGlobalData.ObjectNameZone, SizeOfObjectNameZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return(RC);
|
|
}
|
|
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(UDFGlobalData.CCBZoneHeader),
|
|
UDFQuadAlign(sizeof(UDFCCB)),
|
|
UDFGlobalData.CCBZone,
|
|
SizeOfCCBZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return(RC);
|
|
}
|
|
|
|
if (!NT_SUCCESS(RC = ExInitializeZone(&(UDFGlobalData.IrpContextZoneHeader),
|
|
UDFQuadAlign(sizeof(UDFIrpContext)),
|
|
UDFGlobalData.IrpContextZone,
|
|
SizeOfIrpContextZone))) {
|
|
// failed the initialization, leave ...
|
|
try_return(RC);
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
if (!NT_SUCCESS(RC)) {
|
|
// invoke the destroy routine now ...
|
|
UDFDestroyZones();
|
|
} else {
|
|
// mark the fact that we have allocated zones ...
|
|
UDFSetFlag(UDFGlobalData.UDFFlags, UDF_DATA_FLAGS_ZONES_INITIALIZED);
|
|
}
|
|
} _SEH2_END;
|
|
|
|
return(RC);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFDestroyZones()
|
|
*
|
|
* 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 UDFDestroyZones(VOID)
|
|
{
|
|
// BrutePoint();
|
|
|
|
_SEH2_TRY {
|
|
// free up each of the pools
|
|
if(UDFGlobalData.ObjectNameZone) {
|
|
DbgFreePool(UDFGlobalData.ObjectNameZone);
|
|
UDFGlobalData.ObjectNameZone = NULL;
|
|
}
|
|
if(UDFGlobalData.CCBZone) {
|
|
DbgFreePool(UDFGlobalData.CCBZone);
|
|
UDFGlobalData.CCBZone = NULL;
|
|
}
|
|
if(UDFGlobalData.IrpContextZone) {
|
|
DbgFreePool(UDFGlobalData.IrpContextZone);
|
|
UDFGlobalData.IrpContextZone = NULL;
|
|
}
|
|
|
|
//try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
UDFGlobalData.UDFFlags &= ~UDF_DATA_FLAGS_ZONES_INITIALIZED;
|
|
} _SEH2_END;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFIsIrpTopLevel()
|
|
*
|
|
* 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
|
|
__fastcall
|
|
UDFIsIrpTopLevel(
|
|
PIRP Irp) // the IRP sent to our dispatch routine
|
|
{
|
|
if(!IoGetTopLevelIrp()) {
|
|
// OK, so we can set ourselves to become the "top level" component
|
|
IoSetTopLevelIrp(Irp);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFExceptionFilter()
|
|
*
|
|
* 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
|
|
UDFExceptionFilter(
|
|
PtrUDFIrpContext PtrIrpContext,
|
|
PEXCEPTION_POINTERS PtrExceptionPointers
|
|
)
|
|
{
|
|
long ReturnCode = EXCEPTION_EXECUTE_HANDLER;
|
|
NTSTATUS ExceptionCode = STATUS_SUCCESS;
|
|
#if defined UDF_DBG || defined PRINT_ALWAYS
|
|
ULONG i;
|
|
|
|
UDFPrint(("UDFExceptionFilter\n"));
|
|
UDFPrint((" Ex. Code: %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionCode));
|
|
UDFPrint((" Ex. Addr: %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionAddress));
|
|
UDFPrint((" Ex. Flag: %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionFlags));
|
|
UDFPrint((" Ex. Pnum: %x\n",PtrExceptionPointers->ExceptionRecord->NumberParameters));
|
|
for(i=0;i<PtrExceptionPointers->ExceptionRecord->NumberParameters;i++) {
|
|
UDFPrint((" %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionInformation[i]));
|
|
}
|
|
#ifdef _X86_
|
|
UDFPrint(("Exception context:\n"));
|
|
if(PtrExceptionPointers->ContextRecord->ContextFlags & CONTEXT_INTEGER) {
|
|
UDFPrint(("EAX=%8.8x ",PtrExceptionPointers->ContextRecord->Eax));
|
|
UDFPrint(("EBX=%8.8x ",PtrExceptionPointers->ContextRecord->Ebx));
|
|
UDFPrint(("ECX=%8.8x ",PtrExceptionPointers->ContextRecord->Ecx));
|
|
UDFPrint(("EDX=%8.8x\n",PtrExceptionPointers->ContextRecord->Edx));
|
|
|
|
UDFPrint(("ESI=%8.8x ",PtrExceptionPointers->ContextRecord->Esi));
|
|
UDFPrint(("EDI=%8.8x ",PtrExceptionPointers->ContextRecord->Edi));
|
|
}
|
|
if(PtrExceptionPointers->ContextRecord->ContextFlags & CONTEXT_CONTROL) {
|
|
UDFPrint(("EBP=%8.8x ",PtrExceptionPointers->ContextRecord->Esp));
|
|
UDFPrint(("ESP=%8.8x\n",PtrExceptionPointers->ContextRecord->Ebp));
|
|
|
|
UDFPrint(("EIP=%8.8x\n",PtrExceptionPointers->ContextRecord->Eip));
|
|
}
|
|
// UDFPrint(("Flags: %s %s ",PtrExceptionPointers->ContextRecord->Eip));
|
|
#endif //_X86_
|
|
|
|
#endif // UDF_DBG
|
|
|
|
// 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;
|
|
UDFSetFlag(PtrIrpContext->IrpContextFlags, UDF_IRP_CONTEXT_EXCEPTION);
|
|
}
|
|
|
|
// check if we should propagate this exception or not
|
|
if (!(FsRtlIsNtstatusExpected(ExceptionCode))) {
|
|
|
|
// better free up the IrpContext now ...
|
|
if (PtrIrpContext) {
|
|
UDFPrint((" UDF Driver internal error\n"));
|
|
BrutePoint();
|
|
} else {
|
|
// we are not ok, propagate this exception.
|
|
// NOTE: we will bring down the machine ...
|
|
ReturnCode = EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
}
|
|
|
|
|
|
// return the appropriate code
|
|
return(ReturnCode);
|
|
} // end UDFExceptionFilter()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFExceptionHandler()
|
|
*
|
|
* 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 UDF
|
|
* driver, We shall 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
|
|
UDFExceptionHandler(
|
|
PtrUDFIrpContext PtrIrpContext,
|
|
PIRP Irp
|
|
)
|
|
{
|
|
// NTSTATUS RC;
|
|
NTSTATUS ExceptionCode = STATUS_INSUFFICIENT_RESOURCES;
|
|
PDEVICE_OBJECT Device;
|
|
PVPB Vpb;
|
|
PETHREAD Thread;
|
|
|
|
UDFPrint(("UDFExceptionHandler \n"));
|
|
|
|
// ASSERT(Irp);
|
|
|
|
if (!Irp) {
|
|
UDFPrint((" !Irp, return\n"));
|
|
ASSERT(!PtrIrpContext);
|
|
return ExceptionCode;
|
|
}
|
|
// If it was a queued close (or something like this) then we need not
|
|
// completing it because of MUST_SUCCEED requirement.
|
|
|
|
if (PtrIrpContext) {
|
|
ExceptionCode = PtrIrpContext->SavedExceptionCode;
|
|
// Free irp context here
|
|
// UDFReleaseIrpContext(PtrIrpContext);
|
|
} else {
|
|
UDFPrint((" complete Irp and return\n"));
|
|
// must be insufficient resources ...?
|
|
ExceptionCode = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Status = ExceptionCode;
|
|
Irp->IoStatus.Information = 0;
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return ExceptionCode;
|
|
}
|
|
|
|
// Check if we are posting this request. One of the following must be true
|
|
// if we are to post a request.
|
|
//
|
|
// - Status code is STATUS_CANT_WAIT and the request is asynchronous
|
|
// or we are forcing this to be posted.
|
|
//
|
|
// - Status code is STATUS_VERIFY_REQUIRED and we are at APC level
|
|
// or higher. Can't wait for IO in the verify path in this case.
|
|
//
|
|
// Set the MORE_PROCESSING flag in the IrpContext to keep if from being
|
|
// deleted if this is a retryable condition.
|
|
|
|
if (ExceptionCode == STATUS_VERIFY_REQUIRED) {
|
|
if (KeGetCurrentIrql() >= APC_LEVEL) {
|
|
UDFPrint((" use UDFPostRequest()\n"));
|
|
ExceptionCode = UDFPostRequest( PtrIrpContext, Irp );
|
|
}
|
|
}
|
|
|
|
// If we posted the request or our caller will retry then just return here.
|
|
if ((ExceptionCode == STATUS_PENDING) ||
|
|
(ExceptionCode == STATUS_CANT_WAIT)) {
|
|
|
|
UDFPrint((" STATUS_PENDING/STATUS_CANT_WAIT, return\n"));
|
|
return ExceptionCode;
|
|
}
|
|
|
|
// Store this error into the Irp for posting back to the Io system.
|
|
Irp->IoStatus.Status = ExceptionCode;
|
|
if (IoIsErrorUserInduced( ExceptionCode )) {
|
|
|
|
// Check for the various error conditions that can be caused by,
|
|
// and possibly resolved my the user.
|
|
if (ExceptionCode == STATUS_VERIFY_REQUIRED) {
|
|
|
|
// Now we are at the top level file system entry point.
|
|
//
|
|
// If we have already posted this request then the device to
|
|
// verify is in the original thread. Find this via the Irp.
|
|
Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread );
|
|
IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL );
|
|
|
|
// If there is no device in that location then check in the
|
|
// current thread.
|
|
if (Device == NULL) {
|
|
|
|
Device = IoGetDeviceToVerify( PsGetCurrentThread() );
|
|
IoSetDeviceToVerify( PsGetCurrentThread(), NULL );
|
|
|
|
ASSERT( Device != NULL );
|
|
|
|
// Let's not BugCheck just because the driver screwed up.
|
|
if (Device == NULL) {
|
|
|
|
UDFPrint((" Device == NULL, return\n"));
|
|
ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR;
|
|
Irp->IoStatus.Status = ExceptionCode;
|
|
Irp->IoStatus.Information = 0;
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
|
|
return ExceptionCode;
|
|
}
|
|
}
|
|
|
|
UDFPrint((" use UDFPerformVerify()\n"));
|
|
// UDFPerformVerify() will do the right thing with the Irp.
|
|
// If we return STATUS_CANT_WAIT then the current thread
|
|
// can retry the request.
|
|
return UDFPerformVerify( PtrIrpContext, Irp, Device );
|
|
}
|
|
|
|
//
|
|
// The other user induced conditions generate an error unless
|
|
// they have been disabled for this request.
|
|
//
|
|
|
|
if (FlagOn( PtrIrpContext->IrpContextFlags, UDF_IRP_CONTEXT_FLAG_DISABLE_POPUPS )) {
|
|
|
|
UDFPrint((" DISABLE_POPUPS, complete Irp and return\n"));
|
|
Irp->IoStatus.Status = ExceptionCode;
|
|
Irp->IoStatus.Information = 0;
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
return ExceptionCode;
|
|
} else {
|
|
|
|
// Generate a pop-up
|
|
if (IoGetCurrentIrpStackLocation( Irp )->FileObject != NULL) {
|
|
|
|
Vpb = IoGetCurrentIrpStackLocation( Irp )->FileObject->Vpb;
|
|
} else {
|
|
|
|
Vpb = NULL;
|
|
}
|
|
// The device to verify is either in my thread local storage
|
|
// or that of the thread that owns the Irp.
|
|
Thread = Irp->Tail.Overlay.Thread;
|
|
Device = IoGetDeviceToVerify( Thread );
|
|
|
|
if (Device == NULL) {
|
|
|
|
Thread = PsGetCurrentThread();
|
|
Device = IoGetDeviceToVerify( Thread );
|
|
ASSERT( Device != NULL );
|
|
|
|
// Let's not BugCheck just because the driver screwed up.
|
|
if (Device == NULL) {
|
|
UDFPrint((" Device == NULL, return(2)\n"));
|
|
Irp->IoStatus.Status = ExceptionCode;
|
|
Irp->IoStatus.Information = 0;
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
|
|
return ExceptionCode;
|
|
}
|
|
}
|
|
|
|
// This routine actually causes the pop-up. It usually
|
|
// does this by queuing an APC to the callers thread,
|
|
// but in some cases it will complete the request immediately,
|
|
// so it is very important to IoMarkIrpPending() first.
|
|
IoMarkIrpPending( Irp );
|
|
IoRaiseHardError( Irp, Vpb, Device );
|
|
|
|
// We will be handing control back to the caller here, so
|
|
// reset the saved device object.
|
|
|
|
UDFPrint((" use IoSetDeviceToVerify()\n"));
|
|
IoSetDeviceToVerify( Thread, NULL );
|
|
// The Irp will be completed by Io or resubmitted. In either
|
|
// case we must clean up the IrpContext here.
|
|
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
// If it was a normal request from IOManager then complete it
|
|
if (Irp) {
|
|
UDFPrint((" complete Irp\n"));
|
|
// set the error code in the IRP
|
|
Irp->IoStatus.Status = ExceptionCode;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
}
|
|
|
|
UDFPrint((" return from exception handler with code %x\n", ExceptionCode));
|
|
return(ExceptionCode);
|
|
} // end UDFExceptionHandler()
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFLogEvent()
|
|
*
|
|
* Description:
|
|
* Log a message in the NT Event Log. This is a rather simplistic log
|
|
* methodology since we 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
|
|
UDFLogEvent(
|
|
NTSTATUS UDFEventLogId, // the UDF private message id
|
|
NTSTATUS RC) // any NT error code we wish to log ...
|
|
{
|
|
_SEH2_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).
|
|
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
// nothing really we can do here, just do not wish to crash ...
|
|
NOTHING;
|
|
} _SEH2_END;
|
|
|
|
return;
|
|
} // end UDFLogEvent()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFAllocateObjectName()
|
|
*
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrUDFObjectName
|
|
UDFAllocateObjectName(VOID)
|
|
{
|
|
PtrUDFObjectName PtrObjectName = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
KIRQL CurrentIrql;
|
|
|
|
// first, __try to allocate out of the zone
|
|
KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(UDFGlobalData.ObjectNameZoneHeader))) {
|
|
// we have enough memory
|
|
PtrObjectName = (PtrUDFObjectName)ExAllocateFromZone(&(UDFGlobalData.ObjectNameZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
PtrObjectName = (PtrUDFObjectName)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFObjectName)));
|
|
AllocatedFromZone = FALSE;
|
|
}
|
|
|
|
if (!PtrObjectName) {
|
|
return NULL;
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(PtrObjectName, UDFQuadAlign(sizeof(UDFObjectName)));
|
|
|
|
// set up some fields ...
|
|
PtrObjectName->NodeIdentifier.NodeType = UDF_NODE_TYPE_OBJECT_NAME;
|
|
PtrObjectName->NodeIdentifier.NodeSize = UDFQuadAlign(sizeof(UDFObjectName));
|
|
|
|
|
|
if (!AllocatedFromZone) {
|
|
UDFSetFlag(PtrObjectName->ObjectNameFlags, UDF_OBJ_NAME_NOT_FROM_ZONE);
|
|
}
|
|
|
|
return(PtrObjectName);
|
|
} // end UDFAllocateObjectName()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFReleaseObjectName()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
__fastcall
|
|
UDFReleaseObjectName(
|
|
PtrUDFObjectName PtrObjectName)
|
|
{
|
|
KIRQL CurrentIrql;
|
|
|
|
ASSERT(PtrObjectName);
|
|
|
|
// give back memory either to the zone or to the VMM
|
|
if (!(PtrObjectName->ObjectNameFlags & UDF_OBJ_NAME_NOT_FROM_ZONE)) {
|
|
// back to the zone
|
|
KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(UDFGlobalData.ObjectNameZoneHeader), PtrObjectName);
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
MyFreePool__(PtrObjectName);
|
|
}
|
|
|
|
return;
|
|
} // end UDFReleaseObjectName()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFAllocateCCB()
|
|
*
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrUDFCCB
|
|
UDFAllocateCCB(VOID)
|
|
{
|
|
PtrUDFCCB Ccb = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
KIRQL CurrentIrql;
|
|
|
|
// first, __try to allocate out of the zone
|
|
KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(UDFGlobalData.CCBZoneHeader))) {
|
|
// we have enough memory
|
|
Ccb = (PtrUDFCCB)ExAllocateFromZone(&(UDFGlobalData.CCBZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
Ccb = (PtrUDFCCB)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFCCB)));
|
|
AllocatedFromZone = FALSE;
|
|
// UDFPrint((" CCB allocated @%x\n",Ccb));
|
|
}
|
|
|
|
if (!Ccb) {
|
|
return NULL;
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(Ccb, UDFQuadAlign(sizeof(UDFCCB)));
|
|
|
|
// set up some fields ...
|
|
Ccb->NodeIdentifier.NodeType = UDF_NODE_TYPE_CCB;
|
|
Ccb->NodeIdentifier.NodeSize = UDFQuadAlign(sizeof(UDFCCB));
|
|
|
|
|
|
if (!AllocatedFromZone) {
|
|
UDFSetFlag(Ccb->CCBFlags, UDF_CCB_NOT_FROM_ZONE);
|
|
}
|
|
|
|
UDFPrint(("UDFAllocateCCB: %x\n", Ccb));
|
|
return(Ccb);
|
|
} // end UDFAllocateCCB()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFReleaseCCB()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
__fastcall
|
|
UDFReleaseCCB(
|
|
PtrUDFCCB Ccb
|
|
)
|
|
{
|
|
KIRQL CurrentIrql;
|
|
|
|
ASSERT(Ccb);
|
|
|
|
UDFPrint(("UDFReleaseCCB: %x\n", Ccb));
|
|
// give back memory either to the zone or to the VMM
|
|
if(!(Ccb->CCBFlags & UDF_CCB_NOT_FROM_ZONE)) {
|
|
// back to the zone
|
|
KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(UDFGlobalData.CCBZoneHeader), Ccb);
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
MyFreePool__(Ccb);
|
|
}
|
|
|
|
return;
|
|
} // end UDFReleaseCCB()
|
|
|
|
/*
|
|
Function: UDFCleanupCCB()
|
|
|
|
Description:
|
|
Cleanup and deallocate a previously allocated structure.
|
|
|
|
Expected Interrupt Level (for execution) :
|
|
|
|
IRQL_PASSIVE_LEVEL
|
|
|
|
Return Value: None
|
|
|
|
*/
|
|
VOID
|
|
__fastcall
|
|
UDFCleanUpCCB(
|
|
PtrUDFCCB Ccb)
|
|
{
|
|
// ASSERT(Ccb);
|
|
if(!Ccb) return; // probably, we havn't allocated it...
|
|
ASSERT(Ccb->NodeIdentifier.NodeType == UDF_NODE_TYPE_CCB);
|
|
|
|
_SEH2_TRY {
|
|
if(Ccb->Fcb) {
|
|
UDFTouch(&(Ccb->Fcb->CcbListResource));
|
|
UDFAcquireResourceExclusive(&(Ccb->Fcb->CcbListResource),TRUE);
|
|
RemoveEntryList(&(Ccb->NextCCB));
|
|
UDFReleaseResource(&(Ccb->Fcb->CcbListResource));
|
|
} else {
|
|
BrutePoint();
|
|
}
|
|
|
|
if (Ccb->DirectorySearchPattern) {
|
|
if (Ccb->DirectorySearchPattern->Buffer) {
|
|
MyFreePool__(Ccb->DirectorySearchPattern->Buffer);
|
|
Ccb->DirectorySearchPattern->Buffer = NULL;
|
|
}
|
|
|
|
MyFreePool__(Ccb->DirectorySearchPattern);
|
|
Ccb->DirectorySearchPattern = NULL;
|
|
}
|
|
|
|
UDFReleaseCCB(Ccb);
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
} // end UDFCleanUpCCB()
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFAllocateFCB()
|
|
*
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrUDFFCB
|
|
UDFAllocateFCB(VOID)
|
|
{
|
|
PtrUDFFCB Fcb = NULL;
|
|
|
|
Fcb = (PtrUDFFCB)MyAllocatePool__(UDF_FCB_MT, UDFQuadAlign(sizeof(UDFFCB)));
|
|
|
|
if (!Fcb) {
|
|
return NULL;
|
|
}
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(Fcb, UDFQuadAlign(sizeof(UDFFCB)));
|
|
|
|
// set up some fields ...
|
|
Fcb->NodeIdentifier.NodeType = UDF_NODE_TYPE_FCB;
|
|
Fcb->NodeIdentifier.NodeSize = UDFQuadAlign(sizeof(UDFFCB));
|
|
|
|
UDFPrint(("UDFAllocateFCB: %x\n", Fcb));
|
|
return(Fcb);
|
|
} // end UDFAllocateFCB()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFReleaseFCB()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
/*VOID
|
|
UDFReleaseFCB(
|
|
PtrUDFFCB Fcb
|
|
)
|
|
{
|
|
ASSERT(Fcb);
|
|
|
|
MyFreePool__(Fcb);
|
|
|
|
return;
|
|
}*/
|
|
|
|
/*************************************************************************
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
__fastcall
|
|
UDFCleanUpFCB(
|
|
PtrUDFFCB Fcb
|
|
)
|
|
{
|
|
UDFPrint(("UDFCleanUpFCB: %x\n", Fcb));
|
|
if(!Fcb) return;
|
|
|
|
ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB);
|
|
|
|
_SEH2_TRY {
|
|
// Deinitialize FCBName field
|
|
if (Fcb->FCBName) {
|
|
if(Fcb->FCBName->ObjectName.Buffer) {
|
|
MyFreePool__(Fcb->FCBName->ObjectName.Buffer);
|
|
Fcb->FCBName->ObjectName.Buffer = NULL;
|
|
#ifdef UDF_DBG
|
|
Fcb->FCBName->ObjectName.Length =
|
|
Fcb->FCBName->ObjectName.MaximumLength = 0;
|
|
#endif
|
|
}
|
|
#ifdef UDF_DBG
|
|
else {
|
|
UDFPrint(("UDF: Fcb has invalid FCBName Buffer\n"));
|
|
BrutePoint();
|
|
}
|
|
#endif
|
|
UDFReleaseObjectName(Fcb->FCBName);
|
|
Fcb->FCBName = NULL;
|
|
}
|
|
#ifdef UDF_DBG
|
|
else {
|
|
UDFPrint(("UDF: Fcb has invalid FCBName field\n"));
|
|
BrutePoint();
|
|
}
|
|
#endif
|
|
|
|
|
|
// begin transaction {
|
|
UDFTouch(&(Fcb->Vcb->FcbListResource));
|
|
UDFAcquireResourceExclusive(&(Fcb->Vcb->FcbListResource), TRUE);
|
|
// Remove this FCB from list of all FCB in VCB
|
|
RemoveEntryList(&(Fcb->NextFCB));
|
|
UDFReleaseResource(&(Fcb->Vcb->FcbListResource));
|
|
// } end transaction
|
|
|
|
if(Fcb->FCBFlags & UDF_FCB_INITIALIZED_CCB_LIST_RESOURCE)
|
|
UDFDeleteResource(&(Fcb->CcbListResource));
|
|
|
|
// Free memory
|
|
UDFReleaseFCB(Fcb);
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
} // end UDFCleanUpFCB()
|
|
|
|
#ifdef UDF_DBG
|
|
ULONG IrpContextCounter = 0;
|
|
#endif //UDF_DBG
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFAllocateIrpContext()
|
|
*
|
|
* Description:
|
|
* The UDF FSD creates an IRP context for each request received. This
|
|
* routine simply allocates (and initializes to NULL) a UDFIrpContext
|
|
* 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.
|
|
*
|
|
*************************************************************************/
|
|
PtrUDFIrpContext
|
|
UDFAllocateIrpContext(
|
|
PIRP Irp,
|
|
PDEVICE_OBJECT PtrTargetDeviceObject
|
|
)
|
|
{
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
BOOLEAN AllocatedFromZone = TRUE;
|
|
KIRQL CurrentIrql;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
|
|
// first, __try to allocate out of the zone
|
|
KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
if (!ExIsFullZone(&(UDFGlobalData.IrpContextZoneHeader))) {
|
|
// we have enough memory
|
|
PtrIrpContext = (PtrUDFIrpContext)ExAllocateFromZone(&(UDFGlobalData.IrpContextZoneHeader));
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
// release the spinlock
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
|
|
// if we failed to obtain from the zone, get it directly from the VMM
|
|
PtrIrpContext = (PtrUDFIrpContext)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFIrpContext)));
|
|
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) {
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef UDF_DBG
|
|
IrpContextCounter++;
|
|
#endif //UDF_DBG
|
|
|
|
// zero out the allocated memory block
|
|
RtlZeroMemory(PtrIrpContext, UDFQuadAlign(sizeof(UDFIrpContext)));
|
|
|
|
// set up some fields ...
|
|
PtrIrpContext->NodeIdentifier.NodeType = UDF_NODE_TYPE_IRP_CONTEXT;
|
|
PtrIrpContext->NodeIdentifier.NodeSize = UDFQuadAlign(sizeof(UDFIrpContext));
|
|
|
|
|
|
PtrIrpContext->Irp = Irp;
|
|
PtrIrpContext->TargetDeviceObject = PtrTargetDeviceObject;
|
|
|
|
// copy over some fields from the IRP and set appropriate flag values
|
|
if (Irp) {
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
ASSERT(IrpSp);
|
|
|
|
PtrIrpContext->MajorFunction = IrpSp->MajorFunction;
|
|
PtrIrpContext->MinorFunction = IrpSp->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 our FSD (just like all other FSD
|
|
// implementations on the Windows NT system) has to override the flag
|
|
// below.
|
|
if (IrpSp->FileObject == NULL) {
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK;
|
|
} else {
|
|
if (IoIsOperationSynchronous(Irp)) {
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!AllocatedFromZone) {
|
|
UDFSetFlag(PtrIrpContext->IrpContextFlags, UDF_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
|
|
UDFSetFlag(PtrIrpContext->IrpContextFlags, UDF_IRP_CONTEXT_NOT_TOP_LEVEL);
|
|
}
|
|
|
|
return(PtrIrpContext);
|
|
} // end UDFAllocateIrpContext()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFReleaseIrpContext()
|
|
*
|
|
* Description:
|
|
* Deallocate a previously allocated structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
UDFReleaseIrpContext(
|
|
PtrUDFIrpContext PtrIrpContext)
|
|
{
|
|
if(!PtrIrpContext) return;
|
|
// ASSERT(PtrIrpContext);
|
|
|
|
#ifdef UDF_DBG
|
|
IrpContextCounter--;
|
|
#endif //UDF_DBG
|
|
|
|
// give back memory either to the zone or to the VMM
|
|
if (!(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_FROM_ZONE)) {
|
|
// back to the zone
|
|
KIRQL CurrentIrql;
|
|
|
|
KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
|
|
ExFreeToZone(&(UDFGlobalData.IrpContextZoneHeader), PtrIrpContext);
|
|
KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
|
|
} else {
|
|
MyFreePool__(PtrIrpContext);
|
|
}
|
|
|
|
return;
|
|
} // end UDFReleaseIrpContext()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFPostRequest()
|
|
*
|
|
* 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
|
|
UDFPostRequest(
|
|
IN PtrUDFIrpContext PtrIrpContext,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
KIRQL SavedIrql;
|
|
// PIO_STACK_LOCATION IrpSp;
|
|
PVCB Vcb;
|
|
|
|
// IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
/*
|
|
if(Vcb->StopOverflowQueue) {
|
|
if(Irp) {
|
|
Irp->IoStatus.Status = STATUS_WRONG_VOLUME;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
return STATUS_WRONG_VOLUME;
|
|
}
|
|
*/
|
|
// mark the IRP pending if this is not double post
|
|
if(Irp)
|
|
IoMarkIrpPending(Irp);
|
|
|
|
Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
|
|
KeAcquireSpinLock(&(Vcb->OverflowQueueSpinLock), &SavedIrql);
|
|
|
|
if ( Vcb->PostedRequestCount > FSP_PER_DEVICE_THRESHOLD) {
|
|
|
|
// We cannot currently respond to this IRP so we'll just enqueue it
|
|
// to the overflow queue on the volume.
|
|
// Note: we just reuse LIST_ITEM field inside WorkQueueItem, this
|
|
// doesn't matter to regular processing of WorkItems.
|
|
InsertTailList( &(Vcb->OverflowQueue),
|
|
&(PtrIrpContext->WorkQueueItem.List) );
|
|
Vcb->OverflowQueueCount++;
|
|
KeReleaseSpinLock( &(Vcb->OverflowQueueSpinLock), SavedIrql );
|
|
|
|
} else {
|
|
|
|
// We are going to send this Irp to an ex worker thread so up
|
|
// the count.
|
|
Vcb->PostedRequestCount++;
|
|
|
|
KeReleaseSpinLock( &(Vcb->OverflowQueueSpinLock), SavedIrql );
|
|
|
|
// queue up the request
|
|
ExInitializeWorkItem(&(PtrIrpContext->WorkQueueItem), UDFCommonDispatch, PtrIrpContext);
|
|
|
|
ExQueueWorkItem(&(PtrIrpContext->WorkQueueItem), CriticalWorkQueue);
|
|
// ExQueueWorkItem(&(PtrIrpContext->WorkQueueItem), DelayedWorkQueue);
|
|
|
|
}
|
|
|
|
// return status pending
|
|
return STATUS_PENDING;
|
|
} // end UDFPostRequest()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFCommonDispatch()
|
|
*
|
|
* 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
|
|
UDFCommonDispatch(
|
|
IN PVOID Context // actually is a pointer to IRPContext structure
|
|
)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
PIRP Irp = NULL;
|
|
PVCB Vcb;
|
|
KIRQL SavedIrql;
|
|
PLIST_ENTRY Entry;
|
|
BOOLEAN SpinLock = FALSE;
|
|
|
|
// The context must be a pointer to an IrpContext structure
|
|
PtrIrpContext = (PtrUDFIrpContext)Context;
|
|
|
|
// Assert that the Context is legitimate
|
|
if ( !PtrIrpContext ||
|
|
(PtrIrpContext->NodeIdentifier.NodeType != UDF_NODE_TYPE_IRP_CONTEXT) ||
|
|
(PtrIrpContext->NodeIdentifier.NodeSize != UDFQuadAlign(sizeof(UDFIrpContext))) /*||
|
|
!(PtrIrpContext->Irp)*/) {
|
|
UDFPrint((" Invalid Context\n"));
|
|
BrutePoint();
|
|
return;
|
|
}
|
|
|
|
Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
|
|
ASSERT(Vcb);
|
|
|
|
UDFPrint((" *** Thr: %x ThCnt: %x QCnt: %x Started!\n", PsGetCurrentThread(), Vcb->PostedRequestCount, Vcb->OverflowQueueCount));
|
|
|
|
while(TRUE) {
|
|
|
|
UDFPrint((" Next IRP\n"));
|
|
FsRtlEnterFileSystem();
|
|
|
|
// Get a pointer to the IRP structure
|
|
// in some cases we can get Zero pointer to Irp
|
|
Irp = PtrIrpContext->Irp;
|
|
// 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 & UDF_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);
|
|
} else {
|
|
IoSetTopLevelIrp(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
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK;
|
|
|
|
_SEH2_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
|
|
UDFPrint((" *** MJ: %x, Thr: %x\n", PtrIrpContext->MajorFunction, PsGetCurrentThread()));
|
|
switch (PtrIrpContext->MajorFunction) {
|
|
case IRP_MJ_CREATE:
|
|
// Invoke the common create routine
|
|
RC = UDFCommonCreate(PtrIrpContext, Irp);
|
|
break;
|
|
case IRP_MJ_READ:
|
|
// Invoke the common read routine
|
|
RC = UDFCommonRead(PtrIrpContext, Irp);
|
|
break;
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
case IRP_MJ_WRITE:
|
|
// Invoke the common write routine
|
|
RC = UDFCommonWrite(PtrIrpContext, Irp);
|
|
break;
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
case IRP_MJ_CLEANUP:
|
|
// Invoke the common cleanup routine
|
|
RC = UDFCommonCleanup(PtrIrpContext, Irp);
|
|
break;
|
|
case IRP_MJ_CLOSE:
|
|
// Invoke the common close routine
|
|
RC = UDFCommonClose(PtrIrpContext, Irp);
|
|
break;
|
|
case IRP_MJ_DIRECTORY_CONTROL:
|
|
// Invoke the common directory control routine
|
|
RC = UDFCommonDirControl(PtrIrpContext, Irp);
|
|
break;
|
|
case IRP_MJ_QUERY_INFORMATION:
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
case IRP_MJ_SET_INFORMATION:
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
// Invoke the common query/set information routine
|
|
RC = UDFCommonFileInfo(PtrIrpContext, Irp);
|
|
break;
|
|
case IRP_MJ_QUERY_VOLUME_INFORMATION:
|
|
// Invoke the common query volume routine
|
|
RC = UDFCommonQueryVolInfo(PtrIrpContext, Irp);
|
|
break;
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
case IRP_MJ_SET_VOLUME_INFORMATION:
|
|
// Invoke the common query volume routine
|
|
RC = UDFCommonSetVolInfo(PtrIrpContext, Irp);
|
|
break;
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
#ifdef UDF_HANDLE_EAS
|
|
/* case IRP_MJ_QUERY_EA:
|
|
// Invoke the common query EAs routine
|
|
RC = UDFCommonGetExtendedAttr(PtrIrpContext, Irp);
|
|
break;
|
|
case IRP_MJ_SET_EA:
|
|
// Invoke the common set EAs routine
|
|
RC = UDFCommonSetExtendedAttr(PtrIrpContext, Irp);
|
|
break;*/
|
|
#endif // UDF_HANDLE_EAS
|
|
#ifdef UDF_ENABLE_SECURITY
|
|
case IRP_MJ_QUERY_SECURITY:
|
|
// Invoke the common query Security routine
|
|
RC = UDFCommonGetSecurity(PtrIrpContext, Irp);
|
|
break;
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
case IRP_MJ_SET_SECURITY:
|
|
// Invoke the common set Security routine
|
|
RC = UDFCommonSetSecurity(PtrIrpContext, Irp);
|
|
break;
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
#endif // UDF_ENABLE_SECURITY
|
|
// Continue with the remaining possible dispatch routines below ...
|
|
default:
|
|
UDFPrint((" unhandled *** MJ: %x, Thr: %x\n", PtrIrpContext->MajorFunction, PsGetCurrentThread()));
|
|
// This is the case where we have an invalid major function
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
// Free up the Irp Context
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
break;
|
|
}
|
|
|
|
// Note: PtrIrpContext is invalid here
|
|
UDFPrint((" *** Thr: %x Done!\n", PsGetCurrentThread()));
|
|
|
|
} _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, Irp);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
} _SEH2_END;
|
|
|
|
// Enable preemption
|
|
FsRtlExitFileSystem();
|
|
|
|
// Ensure that the "top-level" field is cleared
|
|
IoSetTopLevelIrp(NULL);
|
|
|
|
// If there are any entries on this volume's overflow queue, service
|
|
// them.
|
|
if(!Vcb) {
|
|
BrutePoint();
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock(&(Vcb->OverflowQueueSpinLock), &SavedIrql);
|
|
SpinLock = TRUE;
|
|
if(!Vcb->OverflowQueueCount)
|
|
break;
|
|
|
|
Vcb->OverflowQueueCount--;
|
|
Entry = RemoveHeadList(&Vcb->OverflowQueue);
|
|
KeReleaseSpinLock(&(Vcb->OverflowQueueSpinLock), SavedIrql);
|
|
SpinLock = FALSE;
|
|
|
|
PtrIrpContext = CONTAINING_RECORD( Entry,
|
|
UDFIrpContext,
|
|
WorkQueueItem.List );
|
|
}
|
|
|
|
if(!SpinLock)
|
|
KeAcquireSpinLock(&(Vcb->OverflowQueueSpinLock), &SavedIrql);
|
|
Vcb->PostedRequestCount--;
|
|
KeReleaseSpinLock(&(Vcb->OverflowQueueSpinLock), SavedIrql);
|
|
|
|
UDFPrint((" *** Thr: %x ThCnt: %x QCnt: %x Terminated!\n", PsGetCurrentThread(), Vcb->PostedRequestCount, Vcb->OverflowQueueCount));
|
|
|
|
return;
|
|
} // end UDFCommonDispatch()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFInitializeVCB()
|
|
*
|
|
* Description:
|
|
* Perform the initialization for a VCB structure.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL PASSIVE_LEVEL
|
|
*
|
|
* Return Value: status
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
UDFInitializeVCB(
|
|
IN PDEVICE_OBJECT PtrVolumeDeviceObject,
|
|
IN PDEVICE_OBJECT PtrTargetDeviceObject,
|
|
IN PVPB PtrVPB
|
|
)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PVCB Vcb = NULL;
|
|
SHORT i;
|
|
|
|
BOOLEAN VCBResourceInit = FALSE;
|
|
BOOLEAN BitMapResource1Init = FALSE;
|
|
BOOLEAN FcbListResourceInit = FALSE;
|
|
BOOLEAN FileIdResourceInit = FALSE;
|
|
BOOLEAN DlocResourceInit = FALSE;
|
|
BOOLEAN DlocResource2Init = FALSE;
|
|
BOOLEAN FlushResourceInit = FALSE;
|
|
BOOLEAN PreallocResourceInit= FALSE;
|
|
BOOLEAN IoResourceInit = FALSE;
|
|
|
|
Vcb = (PVCB)(PtrVolumeDeviceObject->DeviceExtension);
|
|
|
|
_SEH2_TRY {
|
|
// Zero it out (typically this has already been done by the I/O
|
|
// Manager but it does not hurt to do it again)!
|
|
RtlZeroMemory(Vcb, sizeof(VCB));
|
|
|
|
// Initialize the signature fields
|
|
Vcb->NodeIdentifier.NodeType = UDF_NODE_TYPE_VCB;
|
|
Vcb->NodeIdentifier.NodeSize = sizeof(VCB);
|
|
|
|
// Initialize the ERESOURCE object.
|
|
RC = UDFInitializeResourceLite(&(Vcb->VCBResource));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
VCBResourceInit = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->BitMapResource1));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
BitMapResource1Init = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->FcbListResource));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
FcbListResourceInit = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->FileIdResource));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
FileIdResourceInit = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->DlocResource));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
DlocResourceInit = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->DlocResource2));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
DlocResource2Init = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->FlushResource));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
FlushResourceInit = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->PreallocResource));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
PreallocResourceInit = TRUE;
|
|
|
|
RC = UDFInitializeResourceLite(&(Vcb->IoResource));
|
|
if(!NT_SUCCESS(RC))
|
|
try_return(RC);
|
|
IoResourceInit = TRUE;
|
|
|
|
// RC = UDFInitializeResourceLite(&(Vcb->DelayedCloseResource));
|
|
// ASSERT(NT_SUCCESS(RC));
|
|
|
|
// Allocate buffer for statistics
|
|
Vcb->Statistics = (PFILE_SYSTEM_STATISTICS)MyAllocatePool__(NonPagedPool, sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors );
|
|
if(!Vcb->Statistics)
|
|
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
|
|
RtlZeroMemory( Vcb->Statistics, sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors );
|
|
for (i=0; i < (KeNumberProcessors); i++) {
|
|
Vcb->Statistics[i].Common.FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
|
|
Vcb->Statistics[i].Common.Version = 1;
|
|
Vcb->Statistics[i].Common.SizeOfCompleteStructure =
|
|
sizeof(FILE_SYSTEM_STATISTICS);
|
|
}
|
|
|
|
// 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.
|
|
Vcb->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).
|
|
Vcb->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.
|
|
Vcb->Vpb = PtrVPB;
|
|
// Target Vcb field in Vcb onto itself. This required for check in
|
|
// open/lock/unlock volume dispatch poits
|
|
Vcb->Vcb=Vcb;
|
|
|
|
// Set the removable media flag based on the real device's
|
|
// characteristics
|
|
if (PtrVPB->RealDevice->Characteristics & FILE_REMOVABLE_MEDIA) {
|
|
Vcb->VCBFlags |= UDF_VCB_FLAGS_REMOVABLE_MEDIA;
|
|
}
|
|
|
|
// Initialize the list anchor (head) for some lists in this VCB.
|
|
InitializeListHead(&(Vcb->NextFCB));
|
|
InitializeListHead(&(Vcb->NextNotifyIRP));
|
|
InitializeListHead(&(Vcb->VolumeOpenListHead));
|
|
|
|
// Initialize the overflow queue for the volume
|
|
Vcb->OverflowQueueCount = 0;
|
|
InitializeListHead(&(Vcb->OverflowQueue));
|
|
|
|
Vcb->PostedRequestCount = 0;
|
|
KeInitializeSpinLock(&(Vcb->OverflowQueueSpinLock));
|
|
|
|
// Initialize the notify IRP list mutex
|
|
FsRtlNotifyInitializeSync(&(Vcb->NotifyIRPMutex));
|
|
|
|
// Intilize NtRequiredFCB for this VCB
|
|
Vcb->NTRequiredFCB = (PtrUDFNTRequiredFCB)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFNTRequiredFCB)));
|
|
if(!Vcb->NTRequiredFCB)
|
|
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
|
|
RtlZeroMemory(Vcb->NTRequiredFCB, UDFQuadAlign(sizeof(UDFNTRequiredFCB)));
|
|
|
|
// Set the initial file size values appropriately. Note that our FSD may
|
|
// wish to guess at the initial amount of information we would like to
|
|
// read from the disk until we have really determined that this a valid
|
|
// logical volume (on disk) that we wish to mount.
|
|
// Vcb->FileSize = Vcb->AllocationSize = ??
|
|
|
|
// We do not want to bother with valid data length callbacks
|
|
// from the Cache Manager for the file stream opened for volume metadata
|
|
// information
|
|
Vcb->NTRequiredFCB->CommonFCBHeader.ValidDataLength.QuadPart = 0x7FFFFFFFFFFFFFFFULL;
|
|
|
|
Vcb->VolumeLockPID = -1;
|
|
|
|
Vcb->VCBOpenCount = 1;
|
|
|
|
Vcb->WCacheMaxBlocks = UDFGlobalData.WCacheMaxBlocks;
|
|
Vcb->WCacheMaxFrames = UDFGlobalData.WCacheMaxFrames;
|
|
Vcb->WCacheBlocksPerFrameSh = UDFGlobalData.WCacheBlocksPerFrameSh;
|
|
Vcb->WCacheFramesToKeepFree = UDFGlobalData.WCacheFramesToKeepFree;
|
|
|
|
// Create a stream file object for this volume.
|
|
//Vcb->PtrStreamFileObject = IoCreateStreamFileObject(NULL,
|
|
// Vcb->Vpb->RealDevice);
|
|
//ASSERT(Vcb->PtrStreamFileObject);
|
|
|
|
// Initialize some important fields in the newly created file object.
|
|
//Vcb->PtrStreamFileObject->FsContext = (PVOID)Vcb;
|
|
//Vcb->PtrStreamFileObject->FsContext2 = NULL;
|
|
//Vcb->PtrStreamFileObject->SectionObjectPointer = &(Vcb->SectionObject);
|
|
|
|
//Vcb->PtrStreamFileObject->Vpb = PtrVPB;
|
|
|
|
// Link this chap onto the global linked list of all VCB structures.
|
|
// We consider that GlobalDataResource was acquired in past
|
|
UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE);
|
|
InsertTailList(&(UDFGlobalData.VCBQueue), &(Vcb->NextVCB));
|
|
|
|
Vcb->TargetDevName.Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool, sizeof(MOUNTDEV_NAME));
|
|
if(!Vcb->TargetDevName.Buffer)
|
|
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_DEVICE_NAME /*IOCTL_MOUNTDEV_QUERY_DEVICE_NAME*/, Vcb->TargetDeviceObject,
|
|
NULL,0,
|
|
(PVOID)(Vcb->TargetDevName.Buffer),sizeof(MOUNTDEV_NAME),
|
|
FALSE, NULL);
|
|
if(!NT_SUCCESS(RC)) {
|
|
|
|
if(RC == STATUS_BUFFER_OVERFLOW) {
|
|
if(!MyReallocPool__((PCHAR)(Vcb->TargetDevName.Buffer), sizeof(MOUNTDEV_NAME),
|
|
(PCHAR*)&(Vcb->TargetDevName.Buffer), Vcb->TargetDevName.Buffer[0]+sizeof(MOUNTDEV_NAME)) ) {
|
|
goto Kill_DevName_buffer;
|
|
}
|
|
|
|
RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_DEVICE_NAME /*IOCTL_MOUNTDEV_QUERY_DEVICE_NAME*/, Vcb->TargetDeviceObject,
|
|
NULL,0,
|
|
(PVOID)(Vcb->TargetDevName.Buffer), Vcb->TargetDevName.Buffer[0]+sizeof(MOUNTDEV_NAME),
|
|
FALSE, NULL);
|
|
if(!NT_SUCCESS(RC))
|
|
goto Kill_DevName_buffer;
|
|
|
|
} else {
|
|
Kill_DevName_buffer:
|
|
if(!MyReallocPool__((PCHAR)Vcb->TargetDevName.Buffer, sizeof(MOUNTDEV_NAME),
|
|
(PCHAR*)&(Vcb->TargetDevName.Buffer), sizeof(REG_NAMELESS_DEV)))
|
|
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
|
|
RtlCopyMemory(Vcb->TargetDevName.Buffer, REG_NAMELESS_DEV, sizeof(REG_NAMELESS_DEV));
|
|
Vcb->TargetDevName.Length = sizeof(REG_NAMELESS_DEV)-sizeof(WCHAR);
|
|
Vcb->TargetDevName.MaximumLength = sizeof(REG_NAMELESS_DEV);
|
|
goto read_reg;
|
|
}
|
|
}
|
|
|
|
Vcb->TargetDevName.MaximumLength =
|
|
(Vcb->TargetDevName.Length = Vcb->TargetDevName.Buffer[0]) + sizeof(WCHAR);
|
|
RtlMoveMemory((PVOID)(Vcb->TargetDevName.Buffer), (PVOID)(Vcb->TargetDevName.Buffer+1), Vcb->TargetDevName.Buffer[0]);
|
|
Vcb->TargetDevName.Buffer[i = (SHORT)(Vcb->TargetDevName.Length/sizeof(WCHAR))] = 0;
|
|
|
|
for(;i>=0;i--) {
|
|
if(Vcb->TargetDevName.Buffer[i] == L'\\') {
|
|
|
|
Vcb->TargetDevName.Length -= i*sizeof(WCHAR);
|
|
RtlMoveMemory((PVOID)(Vcb->TargetDevName.Buffer), (PVOID)(Vcb->TargetDevName.Buffer+i), Vcb->TargetDevName.Length);
|
|
Vcb->TargetDevName.Buffer[Vcb->TargetDevName.Length/sizeof(WCHAR)] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
UDFPrint((" TargetDevName: %S\n", Vcb->TargetDevName.Buffer));
|
|
|
|
// Initialize caching for the stream file object.
|
|
//CcInitializeCacheMap(Vcb->PtrStreamFileObject, (PCC_FILE_SIZES)(&(Vcb->AllocationSize)),
|
|
// TRUE, // We will use pinned access.
|
|
// &(UDFGlobalData.CacheMgrCallBacks), Vcb);
|
|
|
|
read_reg:
|
|
|
|
UDFReleaseResource(&(UDFGlobalData.GlobalDataResource));
|
|
|
|
// Mark the fact that this VCB structure is initialized.
|
|
Vcb->VCBFlags |= UDF_VCB_FLAGS_VCB_INITIALIZED;
|
|
|
|
RC = STATUS_SUCCESS;
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
if(!NT_SUCCESS(RC)) {
|
|
if(Vcb->TargetDevName.Buffer)
|
|
MyFreePool__(Vcb->TargetDevName.Buffer);
|
|
if(Vcb->NTRequiredFCB)
|
|
MyFreePool__(Vcb->NTRequiredFCB);
|
|
if(Vcb->Statistics)
|
|
MyFreePool__(Vcb->Statistics);
|
|
|
|
if(VCBResourceInit)
|
|
UDFDeleteResource(&(Vcb->VCBResource));
|
|
if(BitMapResource1Init)
|
|
UDFDeleteResource(&(Vcb->BitMapResource1));
|
|
if(FcbListResourceInit)
|
|
UDFDeleteResource(&(Vcb->FcbListResource));
|
|
if(FileIdResourceInit)
|
|
UDFDeleteResource(&(Vcb->FileIdResource));
|
|
if(DlocResourceInit)
|
|
UDFDeleteResource(&(Vcb->DlocResource));
|
|
if(DlocResource2Init)
|
|
UDFDeleteResource(&(Vcb->DlocResource2));
|
|
if(FlushResourceInit)
|
|
UDFDeleteResource(&(Vcb->FlushResource));
|
|
if(PreallocResourceInit)
|
|
UDFDeleteResource(&(Vcb->PreallocResource));
|
|
if(IoResourceInit)
|
|
UDFDeleteResource(&(Vcb->IoResource));
|
|
}
|
|
} _SEH2_END;
|
|
|
|
return RC;
|
|
} // end UDFInitializeVCB()
|
|
|
|
UDFFSD_MEDIA_TYPE
|
|
UDFGetMediaClass(
|
|
PVCB Vcb
|
|
)
|
|
{
|
|
switch(Vcb->FsDeviceType) {
|
|
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
|
|
if(Vcb->VCBFlags & (UDF_VCB_FLAGS_VOLUME_READ_ONLY |
|
|
UDF_VCB_FLAGS_MEDIA_READ_ONLY))
|
|
return MediaCdrom;
|
|
if(Vcb->CDR_Mode)
|
|
return MediaCdr;
|
|
if((Vcb->MediaType >= MediaType_UnknownSize_CDR) &&
|
|
(Vcb->MediaType < MediaType_UnknownSize_CDRW)) {
|
|
return MediaCdr;
|
|
}
|
|
if((Vcb->MediaType >= MediaType_UnknownSize_CDRW) &&
|
|
(Vcb->MediaType < MediaType_UnknownSize_Unknown)) {
|
|
return MediaCdrw;
|
|
}
|
|
if(Vcb->MediaClassEx == CdMediaClass_CDR) {
|
|
return MediaCdr;
|
|
}
|
|
if(Vcb->MediaClassEx == CdMediaClass_DVDR ||
|
|
Vcb->MediaClassEx == CdMediaClass_DVDpR ||
|
|
Vcb->MediaClassEx == CdMediaClass_HD_DVDR ||
|
|
Vcb->MediaClassEx == CdMediaClass_BDR) {
|
|
return MediaDvdr;
|
|
}
|
|
if(Vcb->MediaClassEx == CdMediaClass_CDRW) {
|
|
return MediaCdrw;
|
|
}
|
|
if(Vcb->MediaClassEx == CdMediaClass_DVDRW ||
|
|
Vcb->MediaClassEx == CdMediaClass_DVDpRW ||
|
|
Vcb->MediaClassEx == CdMediaClass_DVDRAM ||
|
|
Vcb->MediaClassEx == CdMediaClass_HD_DVDRW ||
|
|
Vcb->MediaClassEx == CdMediaClass_HD_DVDRAM ||
|
|
Vcb->MediaClassEx == CdMediaClass_BDRE) {
|
|
return MediaDvdrw;
|
|
}
|
|
//
|
|
if(Vcb->MediaClassEx == CdMediaClass_CDROM ||
|
|
Vcb->MediaClassEx == CdMediaClass_DVDROM ||
|
|
Vcb->MediaClassEx == CdMediaClass_HD_DVDROM ||
|
|
Vcb->MediaClassEx == CdMediaClass_BDROM) {
|
|
return MediaCdrom;
|
|
}
|
|
return MediaCdrom;
|
|
#ifdef UDF_HDD_SUPPORT
|
|
case FILE_DEVICE_DISK_FILE_SYSTEM:
|
|
if(Vcb->TargetDeviceObject->Characteristics & FILE_FLOPPY_DISKETTE)
|
|
return MediaFloppy;
|
|
if(Vcb->TargetDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
|
|
return MediaZip;
|
|
return MediaHdd;
|
|
#endif //UDF_HDD_SUPPORT
|
|
}
|
|
return MediaUnknown;
|
|
} // end UDFGetMediaClass()
|
|
|
|
typedef ULONG
|
|
(*ptrUDFGetParameter)(
|
|
IN PVCB Vcb,
|
|
IN PCWSTR Name,
|
|
IN ULONG DefValue
|
|
);
|
|
|
|
VOID
|
|
UDFUpdateCompatOption(
|
|
PVCB Vcb,
|
|
BOOLEAN Update,
|
|
BOOLEAN UseCfg,
|
|
PCWSTR Name,
|
|
ULONG Flag,
|
|
BOOLEAN Default
|
|
)
|
|
{
|
|
ptrUDFGetParameter UDFGetParameter = UseCfg ? UDFGetCfgParameter : UDFGetRegParameter;
|
|
|
|
if(UDFGetParameter(Vcb, Name, Update ? ((Vcb->CompatFlags & Flag) ? TRUE : FALSE) : Default)) {
|
|
Vcb->CompatFlags |= Flag;
|
|
} else {
|
|
Vcb->CompatFlags &= ~Flag;
|
|
}
|
|
} // end UDFUpdateCompatOption()
|
|
|
|
VOID
|
|
UDFReadRegKeys(
|
|
PVCB Vcb,
|
|
BOOLEAN Update,
|
|
BOOLEAN UseCfg
|
|
)
|
|
{
|
|
ULONG mult = 1;
|
|
ptrUDFGetParameter UDFGetParameter = UseCfg ? UDFGetCfgParameter : UDFGetRegParameter;
|
|
|
|
Vcb->DefaultRegName = UDFMediaClassName[(ULONG)UDFGetMediaClass(Vcb)].ClassName;
|
|
|
|
// Should we use Extended FE by default ?
|
|
Vcb->UseExtendedFE = (UCHAR)UDFGetParameter(Vcb, REG_USEEXTENDEDFE_NAME,
|
|
Update ? Vcb->UseExtendedFE : FALSE);
|
|
if(Vcb->UseExtendedFE != TRUE) Vcb->UseExtendedFE = FALSE;
|
|
// What type of AllocDescs should we use
|
|
Vcb->DefaultAllocMode = (USHORT)UDFGetParameter(Vcb, REG_DEFALLOCMODE_NAME,
|
|
Update ? Vcb->DefaultAllocMode : ICB_FLAG_AD_SHORT);
|
|
if(Vcb->DefaultAllocMode > ICB_FLAG_AD_LONG) Vcb->DefaultAllocMode = ICB_FLAG_AD_SHORT;
|
|
// Default UID & GID to be set on newly created files
|
|
Vcb->DefaultUID = UDFGetParameter(Vcb, UDF_DEFAULT_UID_NAME, Update ? Vcb->DefaultUID : -1);
|
|
Vcb->DefaultGID = UDFGetParameter(Vcb, UDF_DEFAULT_GID_NAME, Update ? Vcb->DefaultGID : -1);
|
|
// FE allocation charge for plain Dirs
|
|
Vcb->FECharge = UDFGetParameter(Vcb, UDF_FE_CHARGE_NAME, Update ? Vcb->FECharge : 0);
|
|
if(!Vcb->FECharge)
|
|
Vcb->FECharge = UDF_DEFAULT_FE_CHARGE;
|
|
// FE allocation charge for Stream Dirs (SDir)
|
|
Vcb->FEChargeSDir = UDFGetParameter(Vcb, UDF_FE_CHARGE_SDIR_NAME,
|
|
Update ? Vcb->FEChargeSDir : 0);
|
|
if(!Vcb->FEChargeSDir)
|
|
Vcb->FEChargeSDir = UDF_DEFAULT_FE_CHARGE_SDIR;
|
|
// How many Deleted entries should contain Directory to make us
|
|
// start packing it.
|
|
Vcb->PackDirThreshold = UDFGetParameter(Vcb, UDF_DIR_PACK_THRESHOLD_NAME,
|
|
Update ? Vcb->PackDirThreshold : 0);
|
|
if(Vcb->PackDirThreshold == 0xffffffff)
|
|
Vcb->PackDirThreshold = UDF_DEFAULT_DIR_PACK_THRESHOLD;
|
|
// The binary exponent for the number of Pages to be read-ahead'ed
|
|
// This information would be sent to System Cache Manager
|
|
if(!Update) {
|
|
Vcb->SystemCacheGran = (1 << UDFGetParameter(Vcb, UDF_READAHEAD_GRAN_NAME, 0)) * PAGE_SIZE;
|
|
if(!Vcb->SystemCacheGran)
|
|
Vcb->SystemCacheGran = UDF_DEFAULT_READAHEAD_GRAN;
|
|
}
|
|
// Timeouts for FreeSpaceBitMap & TheWholeDirTree flushes
|
|
Vcb->BM_FlushPriod = UDFGetParameter(Vcb, UDF_BM_FLUSH_PERIOD_NAME,
|
|
Update ? Vcb->BM_FlushPriod : 0);
|
|
if(!Vcb->BM_FlushPriod) {
|
|
Vcb->BM_FlushPriod = UDF_DEFAULT_BM_FLUSH_TIMEOUT;
|
|
} else
|
|
if(Vcb->BM_FlushPriod == (ULONG)-1) {
|
|
Vcb->BM_FlushPriod = 0;
|
|
}
|
|
Vcb->Tree_FlushPriod = UDFGetParameter(Vcb, UDF_TREE_FLUSH_PERIOD_NAME,
|
|
Update ? Vcb->Tree_FlushPriod : 0);
|
|
if(!Vcb->Tree_FlushPriod) {
|
|
Vcb->Tree_FlushPriod = UDF_DEFAULT_TREE_FLUSH_TIMEOUT;
|
|
} else
|
|
if(Vcb->Tree_FlushPriod == (ULONG)-1) {
|
|
Vcb->Tree_FlushPriod = 0;
|
|
}
|
|
Vcb->SkipCountLimit = UDFGetParameter(Vcb, UDF_NO_UPDATE_PERIOD_NAME,
|
|
Update ? Vcb->SkipCountLimit : 0);
|
|
if(!Vcb->SkipCountLimit)
|
|
Vcb->SkipCountLimit = -1;
|
|
|
|
Vcb->SkipEjectCountLimit = UDFGetParameter(Vcb, UDF_NO_EJECT_PERIOD_NAME,
|
|
Update ? Vcb->SkipEjectCountLimit : 3);
|
|
|
|
if(!Update) {
|
|
// How many threads are allowed to sodomize Disc simultaneously on each CPU
|
|
Vcb->ThreadsPerCpu = UDFGetParameter(Vcb, UDF_FSP_THREAD_PER_CPU_NAME,
|
|
Update ? Vcb->ThreadsPerCpu : 2);
|
|
if(Vcb->ThreadsPerCpu < 2)
|
|
Vcb->ThreadsPerCpu = UDF_DEFAULT_FSP_THREAD_PER_CPU;
|
|
}
|
|
// The mimimum FileSize increment when we'll decide not to allocate
|
|
// on-disk space.
|
|
Vcb->SparseThreshold = UDFGetParameter(Vcb, UDF_SPARSE_THRESHOLD_NAME,
|
|
Update ? Vcb->SparseThreshold : 0);
|
|
if(!Vcb->SparseThreshold)
|
|
Vcb->SparseThreshold = UDF_DEFAULT_SPARSE_THRESHOLD;
|
|
// This option is used to VERIFY all the data written. It decreases performance
|
|
Vcb->VerifyOnWrite = UDFGetParameter(Vcb, UDF_VERIFY_ON_WRITE_NAME,
|
|
Update ? Vcb->VerifyOnWrite : FALSE) ? TRUE : FALSE;
|
|
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
// Should we update AttrFileTime on Attr changes
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_TIMES_ATTR, UDF_VCB_IC_UPDATE_ATTR_TIME, FALSE);
|
|
// Should we update ModifyFileTime on Writes changes
|
|
// It also affects ARCHIVE bit setting on write operations
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_TIMES_MOD, UDF_VCB_IC_UPDATE_MODIFY_TIME, FALSE);
|
|
// Should we update AccessFileTime on Exec & so on.
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_TIMES_ACCS, UDF_VCB_IC_UPDATE_ACCESS_TIME, FALSE);
|
|
// Should we update Archive bit
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_ATTR_ARCH, UDF_VCB_IC_UPDATE_ARCH_BIT, FALSE);
|
|
// Should we update Dir's Times & Attrs on Modify
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_DIR_TIMES_ATTR_W, UDF_VCB_IC_UPDATE_DIR_WRITE, FALSE);
|
|
// Should we update Dir's Times & Attrs on Access
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_DIR_TIMES_ATTR_R, UDF_VCB_IC_UPDATE_DIR_READ, FALSE);
|
|
// Should we allow user to write into Read-Only Directory
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_ALLOW_WRITE_IN_RO_DIR, UDF_VCB_IC_WRITE_IN_RO_DIR, TRUE);
|
|
// Should we allow user to change Access Time for unchanged Directory
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_ALLOW_UPDATE_TIMES_ACCS_UCHG_DIR, UDF_VCB_IC_UPDATE_UCHG_DIR_ACCESS_TIME, FALSE);
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
// Should we record Allocation Descriptors in W2k-compatible form
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_W2K_COMPAT_ALLOC_DESCS, UDF_VCB_IC_W2K_COMPAT_ALLOC_DESCS, TRUE);
|
|
// Should we read LONG_ADs with invalid PartitionReferenceNumber (generated by Nero Instant Burner)
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_INSTANT_COMPAT_ALLOC_DESCS, UDF_VCB_IC_INSTANT_COMPAT_ALLOC_DESCS, TRUE);
|
|
// Should we make a copy of VolumeLabel in LVD
|
|
// usually only PVD is updated
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_W2K_COMPAT_VLABEL, UDF_VCB_IC_W2K_COMPAT_VLABEL, TRUE);
|
|
// Should we handle or ignore HW_RO flag
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_HANDLE_HW_RO, UDF_VCB_IC_HW_RO, FALSE);
|
|
// Should we handle or ignore SOFT_RO flag
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_HANDLE_SOFT_RO, UDF_VCB_IC_SOFT_RO, TRUE);
|
|
|
|
// Check if we should generate UDF-style or OS-style DOS-names
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_OS_NATIVE_DOS_NAME, UDF_VCB_IC_OS_NATIVE_DOS_NAME, FALSE);
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
// should we force FO_WRITE_THROUGH on removable media
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_FORCE_WRITE_THROUGH_NAME, UDF_VCB_IC_FORCE_WRITE_THROUGH,
|
|
(Vcb->TargetDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) ? TRUE : FALSE
|
|
);
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
// Should we ignore FO_SEQUENTIAL_ONLY
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_IGNORE_SEQUENTIAL_IO, UDF_VCB_IC_IGNORE_SEQUENTIAL_IO, FALSE);
|
|
// Force Read-only mounts
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_FORCE_HW_RO, UDF_VCB_IC_FORCE_HW_RO, FALSE);
|
|
#else //UDF_READ_ONLY_BUILD
|
|
Vcb->CompatFlags |= UDF_VCB_IC_FORCE_HW_RO;
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
// Check if we should send FLUSH request for File/Dir down to
|
|
// underlaying driver
|
|
if(UDFGetParameter(Vcb, UDF_FLUSH_MEDIA,Update ? Vcb->FlushMedia : FALSE)) {
|
|
Vcb->FlushMedia = TRUE;
|
|
} else {
|
|
Vcb->FlushMedia = FALSE;
|
|
}
|
|
// compare data from packet with data to be writen there
|
|
// before physical writing
|
|
if(!UDFGetParameter(Vcb, UDF_COMPARE_BEFORE_WRITE, Update ? Vcb->DoNotCompareBeforeWrite : FALSE)) {
|
|
Vcb->DoNotCompareBeforeWrite = TRUE;
|
|
} else {
|
|
Vcb->DoNotCompareBeforeWrite = FALSE;
|
|
}
|
|
if(!Update) {
|
|
if(UDFGetParameter(Vcb, UDF_CHAINED_IO, TRUE)) {
|
|
Vcb->CacheChainedIo = TRUE;
|
|
}
|
|
|
|
if(UDFGetParameter(Vcb, UDF_FORCE_MOUNT_ALL, FALSE)) {
|
|
Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
|
|
}
|
|
// Should we show Blank.Cd file on damaged/unformatted,
|
|
// but UDF-compatible disks
|
|
Vcb->ShowBlankCd = (UCHAR)UDFGetParameter(Vcb, UDF_SHOW_BLANK_CD, FALSE);
|
|
if(Vcb->ShowBlankCd) {
|
|
Vcb->CompatFlags |= UDF_VCB_IC_SHOW_BLANK_CD;
|
|
if(Vcb->ShowBlankCd > 2) {
|
|
Vcb->ShowBlankCd = 2;
|
|
}
|
|
}
|
|
// Should we wait util CD device return from
|
|
// Becoming Ready state
|
|
if(UDFGetParameter(Vcb, UDF_WAIT_CD_SPINUP, TRUE)) {
|
|
Vcb->CompatFlags |= UDF_VCB_IC_WAIT_CD_SPINUP;
|
|
}
|
|
// Should we remenber bad VDS locations during mount
|
|
// Caching will improve mount performance on bad disks, but
|
|
// will degrade mauntability of unreliable discs
|
|
if(UDFGetParameter(Vcb, UDF_CACHE_BAD_VDS, TRUE)) {
|
|
Vcb->CompatFlags |= UDF_VCB_IC_CACHE_BAD_VDS;
|
|
}
|
|
|
|
// Set partitially damaged volume mount mode
|
|
Vcb->PartitialDamagedVolumeAction = (UCHAR)UDFGetParameter(Vcb, UDF_PART_DAMAGED_BEHAVIOR, UDF_PART_DAMAGED_RW);
|
|
if(Vcb->PartitialDamagedVolumeAction > 2) {
|
|
Vcb->PartitialDamagedVolumeAction = UDF_PART_DAMAGED_RW;
|
|
}
|
|
|
|
// Set partitially damaged volume mount mode
|
|
Vcb->NoFreeRelocationSpaceVolumeAction = (UCHAR)UDFGetParameter(Vcb, UDF_NO_SPARE_BEHAVIOR, UDF_PART_DAMAGED_RW);
|
|
if(Vcb->NoFreeRelocationSpaceVolumeAction > 1) {
|
|
Vcb->NoFreeRelocationSpaceVolumeAction = UDF_PART_DAMAGED_RW;
|
|
}
|
|
|
|
// Set dirty volume mount mode
|
|
if(UDFGetParameter(Vcb, UDF_DIRTY_VOLUME_BEHAVIOR, UDF_PART_DAMAGED_RO)) {
|
|
Vcb->CompatFlags |= UDF_VCB_IC_DIRTY_RO;
|
|
}
|
|
|
|
mult = UDFGetParameter(Vcb, UDF_CACHE_SIZE_MULTIPLIER, 1);
|
|
if(!mult) mult = 1;
|
|
Vcb->WCacheMaxBlocks *= mult;
|
|
Vcb->WCacheMaxFrames *= mult;
|
|
|
|
if(UDFGetParameter(Vcb, UDF_USE_EJECT_BUTTON, TRUE)) {
|
|
Vcb->UseEvent = TRUE;
|
|
}
|
|
}
|
|
return;
|
|
} // end UDFReadRegKeys()
|
|
|
|
ULONG
|
|
UDFGetRegParameter(
|
|
IN PVCB Vcb,
|
|
IN PCWSTR Name,
|
|
IN ULONG DefValue
|
|
)
|
|
{
|
|
return UDFRegCheckParameterValue(&(UDFGlobalData.SavedRegPath),
|
|
Name,
|
|
Vcb ? &(Vcb->TargetDevName) : NULL,
|
|
Vcb ? Vcb->DefaultRegName : NULL,
|
|
DefValue);
|
|
} // end UDFGetRegParameter()
|
|
|
|
ULONG
|
|
UDFGetCfgParameter(
|
|
IN PVCB Vcb,
|
|
IN PCWSTR Name,
|
|
IN ULONG DefValue
|
|
)
|
|
{
|
|
ULONG len;
|
|
CHAR NameA[128];
|
|
ULONG ret_val=0;
|
|
CHAR a;
|
|
BOOLEAN wait_name=TRUE;
|
|
BOOLEAN wait_val=FALSE;
|
|
BOOLEAN wait_nl=FALSE;
|
|
ULONG radix=10;
|
|
ULONG i;
|
|
|
|
PUCHAR Cfg = Vcb->Cfg;
|
|
ULONG Length = Vcb->CfgLength;
|
|
|
|
if(!Cfg || !Length)
|
|
return DefValue;
|
|
|
|
len = wcslen(Name);
|
|
if(len >= sizeof(NameA))
|
|
return DefValue;
|
|
sprintf(NameA, "%S", Name);
|
|
|
|
for(i=0; i<Length; i++) {
|
|
a=Cfg[i];
|
|
switch(a) {
|
|
case '\n':
|
|
case '\r':
|
|
case ',':
|
|
if(wait_val)
|
|
return DefValue;
|
|
continue;
|
|
case ';':
|
|
case '#':
|
|
case '[': // ignore sections for now, treat as comment
|
|
if(!wait_name)
|
|
return DefValue;
|
|
wait_nl = TRUE;
|
|
continue;
|
|
case '=':
|
|
if(!wait_val)
|
|
return DefValue;
|
|
continue;
|
|
case ' ':
|
|
case '\t':
|
|
continue;
|
|
default:
|
|
if(wait_nl)
|
|
continue;
|
|
}
|
|
if(wait_name) {
|
|
if(i+len+2 > Length)
|
|
return DefValue;
|
|
if(RtlCompareMemory(Cfg+i, NameA, len) == len) {
|
|
a=Cfg[i+len];
|
|
switch(a) {
|
|
case '\n':
|
|
case '\r':
|
|
case ',':
|
|
case ';':
|
|
case '#':
|
|
return DefValue;
|
|
case '=':
|
|
case ' ':
|
|
case '\t':
|
|
break;
|
|
default:
|
|
wait_nl = TRUE;
|
|
wait_val = FALSE;
|
|
i+=len;
|
|
continue;
|
|
}
|
|
wait_name = FALSE;
|
|
wait_nl = FALSE;
|
|
wait_val = TRUE;
|
|
i+=len;
|
|
|
|
} else {
|
|
wait_nl = TRUE;
|
|
}
|
|
continue;
|
|
}
|
|
if(wait_val) {
|
|
if(i+3 > Length) {
|
|
if(a=='0' && Cfg[i+1]=='x') {
|
|
i+=2;
|
|
radix=16;
|
|
}
|
|
}
|
|
if(i >= Length) {
|
|
return DefValue;
|
|
}
|
|
while(i<Length) {
|
|
a=Cfg[i];
|
|
switch(a) {
|
|
case '\n':
|
|
case '\r':
|
|
case ' ':
|
|
case '\t':
|
|
case ',':
|
|
case ';':
|
|
case '#':
|
|
if(wait_val)
|
|
return DefValue;
|
|
return ret_val;
|
|
}
|
|
if(a >= '0' && a <= '9') {
|
|
a -= '0';
|
|
} else {
|
|
if(radix != 16)
|
|
return DefValue;
|
|
if(a >= 'a' && a <= 'f') {
|
|
a -= 'a';
|
|
} else
|
|
if(a >= 'A' && a <= 'F') {
|
|
a -= 'A';
|
|
} else {
|
|
return DefValue;
|
|
}
|
|
a += 0x0a;
|
|
}
|
|
ret_val = ret_val*radix + a;
|
|
wait_val = FALSE;
|
|
i++;
|
|
}
|
|
return ret_val;
|
|
}
|
|
}
|
|
return DefValue;
|
|
|
|
} // end UDFGetCfgParameter()
|
|
|
|
VOID
|
|
UDFReleaseVCB(
|
|
PVCB Vcb
|
|
)
|
|
{
|
|
LARGE_INTEGER delay;
|
|
UDFPrint(("UDFReleaseVCB\n"));
|
|
|
|
delay.QuadPart = -500000; // 0.05 sec
|
|
while(Vcb->PostedRequestCount) {
|
|
UDFPrint(("UDFReleaseVCB: PostedRequestCount = %d\n", Vcb->PostedRequestCount));
|
|
// spin until all queues IRPs are processed
|
|
KeDelayExecutionThread(KernelMode, FALSE, &delay);
|
|
delay.QuadPart -= 500000; // grow delay 0.05 sec
|
|
}
|
|
|
|
_SEH2_TRY {
|
|
UDFPrint(("UDF: Flushing buffers\n"));
|
|
UDFVRelease(Vcb);
|
|
WCacheFlushAll__(&(Vcb->FastCache),Vcb);
|
|
WCacheRelease__(&(Vcb->FastCache));
|
|
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
|
|
#ifdef UDF_DBG
|
|
_SEH2_TRY {
|
|
if (!ExIsResourceAcquiredShared(&UDFGlobalData.GlobalDataResource)) {
|
|
UDFPrint(("UDF: attempt to access to not protected data\n"));
|
|
UDFPrint(("UDF: UDFGlobalData\n"));
|
|
BrutePoint();
|
|
}
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
#endif
|
|
|
|
_SEH2_TRY {
|
|
RemoveEntryList(&(Vcb->NextVCB));
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
|
|
/* _SEH2_TRY {
|
|
if(Vcb->VCBFlags & UDF_VCB_FLAGS_STOP_WAITER_EVENT)
|
|
KeWaitForSingleObject(&(Vcb->WaiterStopped), Executive, KernelMode, FALSE, NULL);
|
|
Vcb->VCBFlags &= ~UDF_VCB_FLAGS_STOP_WAITER_EVENT;
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
}*/
|
|
|
|
_SEH2_TRY {
|
|
UDFPrint(("UDF: Delete resources\n"));
|
|
UDFDeleteResource(&(Vcb->VCBResource));
|
|
UDFDeleteResource(&(Vcb->BitMapResource1));
|
|
UDFDeleteResource(&(Vcb->FcbListResource));
|
|
UDFDeleteResource(&(Vcb->FileIdResource));
|
|
UDFDeleteResource(&(Vcb->DlocResource));
|
|
UDFDeleteResource(&(Vcb->DlocResource2));
|
|
UDFDeleteResource(&(Vcb->FlushResource));
|
|
UDFDeleteResource(&(Vcb->PreallocResource));
|
|
UDFDeleteResource(&(Vcb->IoResource));
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
|
|
_SEH2_TRY {
|
|
UDFPrint(("UDF: Cleanup VCB\n"));
|
|
ASSERT(IsListEmpty(&(Vcb->NextNotifyIRP)));
|
|
FsRtlNotifyUninitializeSync(&(Vcb->NotifyIRPMutex));
|
|
UDFCleanupVCB(Vcb);
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
|
|
_SEH2_TRY {
|
|
UDFPrint(("UDF: Delete DO\n"));
|
|
IoDeleteDevice(Vcb->VCBDeviceObject);
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
|
|
} // end UDFReleaseVCB()
|
|
|
|
/*
|
|
Read DWORD from Registry
|
|
*/
|
|
ULONG
|
|
UDFRegCheckParameterValue(
|
|
IN PUNICODE_STRING RegistryPath,
|
|
IN PCWSTR Name,
|
|
IN PUNICODE_STRING PtrVolumePath,
|
|
IN PCWSTR DefaultPath,
|
|
IN ULONG DefValue
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
ULONG val = DefValue;
|
|
|
|
UNICODE_STRING paramStr;
|
|
UNICODE_STRING defaultParamStr;
|
|
UNICODE_STRING paramPathUnknownStr;
|
|
|
|
UNICODE_STRING paramSuffix;
|
|
UNICODE_STRING paramPath;
|
|
UNICODE_STRING paramPathUnknown;
|
|
UNICODE_STRING paramDevPath;
|
|
UNICODE_STRING defaultParamPath;
|
|
|
|
_SEH2_TRY {
|
|
|
|
paramPath.Buffer = NULL;
|
|
paramDevPath.Buffer = NULL;
|
|
paramPathUnknown.Buffer = NULL;
|
|
defaultParamPath.Buffer = NULL;
|
|
|
|
// First append \Parameters to the passed in registry path
|
|
// Note, RtlInitUnicodeString doesn't allocate memory
|
|
RtlInitUnicodeString(¶mStr, L"\\Parameters");
|
|
RtlInitUnicodeString(¶mPath, NULL);
|
|
|
|
RtlInitUnicodeString(¶mPathUnknownStr, REG_DEFAULT_UNKNOWN);
|
|
RtlInitUnicodeString(¶mPathUnknown, NULL);
|
|
|
|
paramPathUnknown.MaximumLength = RegistryPath->Length + paramPathUnknownStr.Length + paramStr.Length + sizeof(WCHAR);
|
|
paramPath.MaximumLength = RegistryPath->Length + paramStr.Length + sizeof(WCHAR);
|
|
|
|
paramPath.Buffer = (PWCH)MyAllocatePool__(PagedPool, paramPath.MaximumLength);
|
|
if(!paramPath.Buffer) {
|
|
UDFPrint(("UDFCheckRegValue: couldn't allocate paramPath\n"));
|
|
try_return(val = DefValue);
|
|
}
|
|
paramPathUnknown.Buffer = (PWCH)MyAllocatePool__(PagedPool, paramPathUnknown.MaximumLength);
|
|
if(!paramPathUnknown.Buffer) {
|
|
UDFPrint(("UDFCheckRegValue: couldn't allocate paramPathUnknown\n"));
|
|
try_return(val = DefValue);
|
|
}
|
|
|
|
RtlZeroMemory(paramPath.Buffer, paramPath.MaximumLength);
|
|
status = RtlAppendUnicodeToString(¶mPath, RegistryPath->Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
status = RtlAppendUnicodeToString(¶mPath, paramStr.Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
UDFPrint(("UDFCheckRegValue: (1) |%S|\n", paramPath.Buffer));
|
|
|
|
RtlZeroMemory(paramPathUnknown.Buffer, paramPathUnknown.MaximumLength);
|
|
status = RtlAppendUnicodeToString(¶mPathUnknown, RegistryPath->Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
status = RtlAppendUnicodeToString(¶mPathUnknown, paramStr.Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
status = RtlAppendUnicodeToString(¶mPathUnknown, paramPathUnknownStr.Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
UDFPrint(("UDFCheckRegValue: (2) |%S|\n", paramPathUnknown.Buffer));
|
|
|
|
// First append \Parameters\Default_XXX to the passed in registry path
|
|
if(DefaultPath) {
|
|
RtlInitUnicodeString(&defaultParamStr, DefaultPath);
|
|
RtlInitUnicodeString(&defaultParamPath, NULL);
|
|
defaultParamPath.MaximumLength = paramPath.Length + defaultParamStr.Length + sizeof(WCHAR);
|
|
defaultParamPath.Buffer = (PWCH)MyAllocatePool__(PagedPool, defaultParamPath.MaximumLength);
|
|
if(!defaultParamPath.Buffer) {
|
|
UDFPrint(("UDFCheckRegValue: couldn't allocate defaultParamPath\n"));
|
|
try_return(val = DefValue);
|
|
}
|
|
|
|
RtlZeroMemory(defaultParamPath.Buffer, defaultParamPath.MaximumLength);
|
|
status = RtlAppendUnicodeToString(&defaultParamPath, paramPath.Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
status = RtlAppendUnicodeToString(&defaultParamPath, defaultParamStr.Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
UDFPrint(("UDFCheckRegValue: (3) |%S|\n", defaultParamPath.Buffer));
|
|
}
|
|
|
|
if(PtrVolumePath) {
|
|
paramSuffix = *PtrVolumePath;
|
|
} else {
|
|
RtlInitUnicodeString(¶mSuffix, NULL);
|
|
}
|
|
|
|
RtlInitUnicodeString(¶mDevPath, NULL);
|
|
// now build the device specific path
|
|
paramDevPath.MaximumLength = paramPath.Length + paramSuffix.Length + sizeof(WCHAR);
|
|
paramDevPath.Buffer = (PWCH)MyAllocatePool__(PagedPool, paramDevPath.MaximumLength);
|
|
if(!paramDevPath.Buffer) {
|
|
try_return(val = DefValue);
|
|
}
|
|
|
|
RtlZeroMemory(paramDevPath.Buffer, paramDevPath.MaximumLength);
|
|
status = RtlAppendUnicodeToString(¶mDevPath, paramPath.Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
if(paramSuffix.Buffer) {
|
|
status = RtlAppendUnicodeToString(¶mDevPath, paramSuffix.Buffer);
|
|
if(!NT_SUCCESS(status)) {
|
|
try_return(val = DefValue);
|
|
}
|
|
}
|
|
|
|
UDFPrint(( " Parameter = %ws\n", Name));
|
|
|
|
{
|
|
HKEY hk = NULL;
|
|
status = RegTGetKeyHandle(NULL, RegistryPath->Buffer, &hk);
|
|
if(NT_SUCCESS(status)) {
|
|
RegTCloseKeyHandle(hk);
|
|
}
|
|
}
|
|
|
|
|
|
// *** Read GLOBAL_DEFAULTS from
|
|
// "\DwUdf\Parameters_Unknown\"
|
|
|
|
status = RegTGetDwordValue(NULL, paramPath.Buffer, Name, &val);
|
|
|
|
// *** Read DEV_CLASS_SPEC_DEFAULTS (if any) from
|
|
// "\DwUdf\Parameters_%DevClass%\"
|
|
|
|
if(DefaultPath) {
|
|
status = RegTGetDwordValue(NULL, defaultParamPath.Buffer, Name, &val);
|
|
}
|
|
|
|
// *** Read DEV_SPEC_PARAMS from (if device supports GetDevName)
|
|
// "\DwUdf\Parameters\%DevName%\"
|
|
|
|
status = RegTGetDwordValue(NULL, paramDevPath.Buffer, Name, &val);
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
if(DefaultPath && defaultParamPath.Buffer) {
|
|
MyFreePool__(defaultParamPath.Buffer);
|
|
}
|
|
if(paramPath.Buffer) {
|
|
MyFreePool__(paramPath.Buffer);
|
|
}
|
|
if(paramDevPath.Buffer) {
|
|
MyFreePool__(paramDevPath.Buffer);
|
|
}
|
|
if(paramPathUnknown.Buffer) {
|
|
MyFreePool__(paramPathUnknown.Buffer);
|
|
}
|
|
} _SEH2_END;
|
|
|
|
UDFPrint(( "UDFCheckRegValue: %ws for drive %s is %x\n\n", Name, PtrVolumePath, val));
|
|
return val;
|
|
} // end UDFRegCheckParameterValue()
|
|
|
|
/*
|
|
Routine Description:
|
|
This routine is called to initialize an IrpContext for the current
|
|
UDFFS request. The IrpContext is on the stack and we need to initialize
|
|
it for the current request. The request is a close operation.
|
|
|
|
Arguments:
|
|
|
|
IrpContext - IrpContext to initialize.
|
|
|
|
IrpContextLite - source for initialization
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
*/
|
|
VOID
|
|
UDFInitializeIrpContextFromLite(
|
|
OUT PtrUDFIrpContext *IrpContext,
|
|
IN PtrUDFIrpContextLite IrpContextLite
|
|
)
|
|
{
|
|
(*IrpContext) = UDFAllocateIrpContext(NULL, IrpContextLite->RealDevice);
|
|
// Zero and then initialize the structure.
|
|
|
|
// Major/Minor Function codes
|
|
(*IrpContext)->MajorFunction = IRP_MJ_CLOSE;
|
|
(*IrpContext)->Fcb = IrpContextLite->Fcb;
|
|
(*IrpContext)->TreeLength = IrpContextLite->TreeLength;
|
|
(*IrpContext)->IrpContextFlags |= (IrpContextLite->IrpContextFlags & ~UDF_IRP_CONTEXT_NOT_FROM_ZONE);
|
|
|
|
// Set the wait parameter
|
|
UDFSetFlag( (*IrpContext)->IrpContextFlags, UDF_IRP_CONTEXT_CAN_BLOCK );
|
|
|
|
return;
|
|
} // end UDFInitializeIrpContextFromLite()
|
|
|
|
/*
|
|
Routine Description:
|
|
This routine is called to initialize an IrpContext for the current
|
|
UDFFS request. The IrpContext is on the stack and we need to initialize
|
|
it for the current request. The request is a close operation.
|
|
|
|
Arguments:
|
|
|
|
IrpContext - IrpContext to initialize.
|
|
|
|
IrpContextLite - source for initialization
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
*/
|
|
NTSTATUS
|
|
UDFInitializeIrpContextLite(
|
|
OUT PtrUDFIrpContextLite *IrpContextLite,
|
|
IN PtrUDFIrpContext IrpContext,
|
|
IN PtrUDFFCB Fcb
|
|
)
|
|
{
|
|
PtrUDFIrpContextLite LocalIrpContextLite = (PtrUDFIrpContextLite)MyAllocatePool__(NonPagedPool,sizeof(UDFIrpContextLite));
|
|
if(!LocalIrpContextLite)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
// Zero and then initialize the structure.
|
|
RtlZeroMemory( LocalIrpContextLite, sizeof( UDFIrpContextLite ));
|
|
|
|
LocalIrpContextLite->NodeIdentifier.NodeType = UDF_NODE_TYPE_IRP_CONTEXT_LITE;
|
|
LocalIrpContextLite->NodeIdentifier.NodeSize = sizeof(UDFIrpContextLite);
|
|
|
|
LocalIrpContextLite->Fcb = Fcb;
|
|
LocalIrpContextLite->TreeLength = IrpContext->TreeLength;
|
|
// Copy RealDevice for workque algorithms.
|
|
LocalIrpContextLite->RealDevice = IrpContext->TargetDeviceObject;
|
|
LocalIrpContextLite->IrpContextFlags = IrpContext->IrpContextFlags;
|
|
*IrpContextLite = LocalIrpContextLite;
|
|
|
|
return STATUS_SUCCESS;
|
|
} // end UDFInitializeIrpContextLite()
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
UDFQuerySetEA(
|
|
PDEVICE_OBJECT DeviceObject, // the logical volume device object
|
|
PIRP Irp // I/O Request Packet
|
|
)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
// PtrUDFIrpContext PtrIrpContext = NULL;
|
|
BOOLEAN AreWeTopLevel = FALSE;
|
|
|
|
UDFPrint(("UDFQuerySetEA: \n"));
|
|
|
|
FsRtlEnterFileSystem();
|
|
ASSERT(DeviceObject);
|
|
ASSERT(Irp);
|
|
|
|
// set the top level context
|
|
AreWeTopLevel = UDFIsIrpTopLevel(Irp);
|
|
|
|
RC = STATUS_EAS_NOT_SUPPORTED;
|
|
Irp->IoStatus.Status = RC;
|
|
Irp->IoStatus.Information = 0;
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
|
|
if(AreWeTopLevel) {
|
|
IoSetTopLevelIrp(NULL);
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(RC);
|
|
} // end UDFQuerySetEA()
|
|
|
|
ULONG
|
|
UDFIsResourceAcquired(
|
|
IN PERESOURCE Resource
|
|
)
|
|
{
|
|
ULONG ReAcqRes =
|
|
ExIsResourceAcquiredExclusiveLite(Resource) ? 1 :
|
|
(ExIsResourceAcquiredSharedLite(Resource) ? 2 : 0);
|
|
return ReAcqRes;
|
|
} // end UDFIsResourceAcquired()
|
|
|
|
BOOLEAN
|
|
UDFAcquireResourceExclusiveWithCheck(
|
|
IN PERESOURCE Resource
|
|
)
|
|
{
|
|
ULONG ReAcqRes =
|
|
ExIsResourceAcquiredExclusiveLite(Resource) ? 1 :
|
|
(ExIsResourceAcquiredSharedLite(Resource) ? 2 : 0);
|
|
if(ReAcqRes) {
|
|
UDFPrint(("UDFAcquireResourceExclusiveWithCheck: ReAcqRes, %x\n", ReAcqRes));
|
|
} else {
|
|
// BrutePoint();
|
|
}
|
|
|
|
if(ReAcqRes == 1) {
|
|
// OK
|
|
} else
|
|
if(ReAcqRes == 2) {
|
|
UDFPrint(("UDFAcquireResourceExclusiveWithCheck: !!! Shared !!!\n"));
|
|
//BrutePoint();
|
|
} else {
|
|
UDFAcquireResourceExclusive(Resource, TRUE);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
} // end UDFAcquireResourceExclusiveWithCheck()
|
|
|
|
BOOLEAN
|
|
UDFAcquireResourceSharedWithCheck(
|
|
IN PERESOURCE Resource
|
|
)
|
|
{
|
|
ULONG ReAcqRes =
|
|
ExIsResourceAcquiredExclusiveLite(Resource) ? 1 :
|
|
(ExIsResourceAcquiredSharedLite(Resource) ? 2 : 0);
|
|
if(ReAcqRes) {
|
|
UDFPrint(("UDFAcquireResourceSharedWithCheck: ReAcqRes, %x\n", ReAcqRes));
|
|
/* } else {
|
|
BrutePoint();*/
|
|
}
|
|
|
|
if(ReAcqRes == 2) {
|
|
// OK
|
|
} else
|
|
if(ReAcqRes == 1) {
|
|
UDFPrint(("UDFAcquireResourceSharedWithCheck: Exclusive\n"));
|
|
//BrutePoint();
|
|
} else {
|
|
UDFAcquireResourceShared(Resource, TRUE);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
} // end UDFAcquireResourceSharedWithCheck()
|
|
|
|
NTSTATUS
|
|
UDFWCacheErrorHandler(
|
|
IN PVOID Context,
|
|
IN PWCACHE_ERROR_CONTEXT ErrorInfo
|
|
)
|
|
{
|
|
InterlockedIncrement((PLONG)&(((PVCB)Context)->IoErrorCounter));
|
|
return ErrorInfo->Status;
|
|
}
|
|
|
|
#include "Include/misc_common.cpp"
|
|
#include "Include/regtools.cpp"
|
|
|