//////////////////////////////////////////////////////////////////// // 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: Shutdown.cpp * * Module: UDF File System Driver (Kernel mode execution only) * * Description: * Contains code to handle the "shutdown notification" dispatch entry point. * *************************************************************************/ #include "udffs.h" // define the file specific bug-check id #define UDF_BUG_CHECK_ID UDF_FILE_SHUTDOWN /************************************************************************* * * Function: UDFShutdown() * * Description: * All disk-based FSDs can expect to receive this shutdown notification * request whenever the system is about to be halted gracefully. If you * design and implement a network redirector, you must register explicitly * for shutdown notification by invoking the IoRegisterShutdownNotification() * routine from your driver entry. * * Note that drivers that register to receive shutdown notification get * invoked BEFORE disk-based FSDs are told about the shutdown notification. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: Irrelevant. * *************************************************************************/ NTSTATUS NTAPI UDFShutdown( 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(("UDFShutDown\n")); // BrutePoint(); FsRtlEnterFileSystem(); ASSERT(DeviceObject); ASSERT(Irp); // set the top level context AreWeTopLevel = UDFIsIrpTopLevel(Irp); //ASSERT(!UDFIsFSDevObj(DeviceObject)); _SEH2_TRY { // get an IRP context structure and issue the request PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject); if(PtrIrpContext) { RC = UDFCommonShutdown(PtrIrpContext, Irp); } else { RC = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; // complete the IRP IoCompleteRequest(Irp, IO_DISK_INCREMENT); } } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) { RC = UDFExceptionHandler(PtrIrpContext, Irp); UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC); } _SEH2_END; if (AreWeTopLevel) { IoSetTopLevelIrp(NULL); } FsRtlExitFileSystem(); return(RC); } // end UDFShutdown() /************************************************************************* * * Function: UDFCommonShutdown() * * Description: * The actual work is performed here. Basically, all we do here is * internally invoke a flush on all mounted logical volumes. This, in * tuen, will result in all open file streams being flushed to disk. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: Irrelevant * *************************************************************************/ NTSTATUS UDFCommonShutdown( PtrUDFIrpContext PtrIrpContext, PIRP Irp ) { NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp = NULL; PVCB Vcb; PLIST_ENTRY Link; PPREVENT_MEDIA_REMOVAL_USER_IN Buf = NULL; LARGE_INTEGER delay; UDFPrint(("UDFCommonShutdown\n")); _SEH2_TRY { // First, get a pointer to the current I/O stack location IrpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(IrpSp); Buf = (PPREVENT_MEDIA_REMOVAL_USER_IN)MyAllocatePool__(NonPagedPool, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN)); if(!Buf) try_return(RC = STATUS_INSUFFICIENT_RESOURCES); // (a) Block all new "mount volume" requests by acquiring an appropriate // global resource/lock. // (b) Go through your linked list of mounted logical volumes and for // each such volume, do the following: // (i) acquire the volume resource exclusively // (ii) invoke UDFFlushLogicalVolume() (internally) to flush the // open data streams belonging to the volume from the system // cache // (iii) Invoke the physical/virtual/logical target device object // on which the volume is mounted and inform this device // about the shutdown request (Use IoBuildSynchronouFsdRequest() // to create an IRP with MajorFunction = IRP_MJ_SHUTDOWN that you // will then issue to the target device object). // (iv) Wait for the completion of the shutdown processing by the target // device object // (v) Release the VCB resource we will have acquired in (i) above. // Acquire GlobalDataResource UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); // Walk through all of the Vcb's attached to the global data. Link = UDFGlobalData.VCBQueue.Flink; while (Link != &(UDFGlobalData.VCBQueue)) { // Get 'next' Vcb Vcb = CONTAINING_RECORD( Link, VCB, NextVCB ); // Move to the next link now since the current Vcb may be deleted. Link = Link->Flink; ASSERT(Link != Link->Flink); if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN)) { #ifdef UDF_DELAYED_CLOSE UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); UDFPrint((" UDFCommonShutdown: set UDF_VCB_FLAGS_NO_DELAYED_CLOSE\n")); Vcb->VCBFlags |= UDF_VCB_FLAGS_NO_DELAYED_CLOSE; UDFReleaseResource(&(Vcb->VCBResource)); #endif //UDF_DELAYED_CLOSE // Note: UDFCloseAllDelayed() doesn't acquire DelayedCloseResource if // GlobalDataResource is already acquired. Thus for now we should // release GlobalDataResource and re-acquire it later. UDFReleaseResource( &(UDFGlobalData.GlobalDataResource) ); if(Vcb->RootDirFCB && Vcb->RootDirFCB->FileInfo) { UDFPrint((" UDFCommonShutdown: UDFCloseAllSystemDelayedInDir\n")); RC = UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); ASSERT(OS_SUCCESS(RC)); } #ifdef UDF_DELAYED_CLOSE UDFCloseAllDelayed(Vcb); // UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource)); #endif //UDF_DELAYED_CLOSE // re-acquire GlobalDataResource UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); // disable Eject Waiter UDFStopEjectWaiter(Vcb); // Acquire Vcb resource UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); ASSERT(!Vcb->OverflowQueueCount); if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN)) { UDFDoDismountSequence(Vcb, Buf, FALSE); if(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) { // let drive flush all data before reset delay.QuadPart = -10000000; // 1 sec KeDelayExecutionThread(KernelMode, FALSE, &delay); } Vcb->VCBFlags |= (UDF_VCB_FLAGS_SHUTDOWN | UDF_VCB_FLAGS_VOLUME_READ_ONLY); } UDFReleaseResource(&(Vcb->VCBResource)); } } // Once we have processed all the mounted logical volumes, we can release // all acquired global resources and leave (in peace :-) UDFReleaseResource( &(UDFGlobalData.GlobalDataResource) ); RC = STATUS_SUCCESS; try_exit: NOTHING; } _SEH2_FINALLY { if(Buf) MyFreePool__(Buf); if(!_SEH2_AbnormalTermination()) { Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); // complete the IRP IoCompleteRequest(Irp, IO_DISK_INCREMENT); } } _SEH2_END; // end of "__finally" processing return(RC); } // end UDFCommonShutdown()