//////////////////////////////////////////////////////////////////// // 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 //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;iExceptionRecord->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) 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= '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"