reactos/drivers/filesystems/udfs/udf_info/phys_eject.cpp

845 lines
35 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.
////////////////////////////////////////////////////////////////////
/*
Module Name: Phys_eject.cpp
Execution: Kernel mode only
Description:
Contains code that implement read/write operations for physical device
*/
#include "udf.h"
// define the file specific bug-check id
#define UDF_BUG_CHECK_ID UDF_FILE_PHYS_EJECT
extern void
UDFKeyWaiter(
IN void* Context
);
/*
This routine checks for User Eject request & initiates Dismount
*/
void
NTAPI
UDFEjectReqWaiter(
IN void* Context
)
{
PUDFEjectWaitContext WC = (PUDFEjectWaitContext)Context;
PVCB Vcb;
OSSTATUS RC = STATUS_SUCCESS;
OSSTATUS WRC;
LARGE_INTEGER delay;
LARGE_INTEGER time;
BOOLEAN UseEvent = TRUE;
uint32 d;
BOOLEAN FlushWCache = FALSE;
IO_STATUS_BLOCK IoStatus;
BOOLEAN VcbAcquired;
BOOLEAN AllFlushed;
PDEVICE_OBJECT TargetDevObj;
uint32 BM_FlushPriod;
uint32 Tree_FlushPriod;
uint32 SkipCount = 0;
uint32 SkipEjectCount = 0;
uint32 flags = 0;
uint32 flush_stat = 0;
BOOLEAN UseEject = TRUE;
BOOLEAN MediaLoss = FALSE;
BOOLEAN SkipEject = FALSE;
BOOLEAN SkipFlush = FALSE;
// BOOLEAN FlushAndEject = FALSE;
UDFPrint((" UDFEjectReqWaiter: start\n"));
uint8 supported_evt_classes = 0;
uint32 i, j;
uint8 evt_type;
BOOLEAN OldLowFreeSpace = FALSE;
uint32 space_check_counter = 0x7fffffff;
PGET_LAST_ERROR_USER_OUT Error = NULL;
// Drain out Event Queue
Vcb = WC->Vcb;
TargetDevObj = Vcb->TargetDeviceObject;
UseEvent = Vcb->UseEvent;
if(UseEvent) {
supported_evt_classes = EventStat_Class_Media;
} else {
UDFPrint((" Eject Button ignored\n"));
}
for(j=0; j<4; j++) {
UDFPrint((" Reading events... (0)\n"));
if(supported_evt_classes) {
for(i=1; i<=EventRetStat_Class_Mask;i++) {
evt_type = (((UCHAR)1) << i);
if( !(supported_evt_classes & evt_type) )
continue;
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = evt_type;
RC = UDFPhSendIOCTL( IOCTL_CDRW_GET_EVENT,
TargetDevObj,
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
FALSE,NULL);
if(RC == STATUS_INVALID_DEVICE_REQUEST) {
UseEvent = FALSE;
break;
}
if(RC == STATUS_NO_SUCH_DEVICE)
break;
if(OS_SUCCESS(RC)) {
supported_evt_classes = WC->EjectReqBuffer.MediaChange.Header.SupportedClasses;
}
}
}
if(!UseEvent)
break;
if(RC == STATUS_NO_SUCH_DEVICE)
break;
}
supported_evt_classes = 0;
// Wait for events
while(TRUE) {
_SEH2_TRY {
VcbAcquired = FALSE;
delay.QuadPart = -10000000; // 1.0 sec
WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
if(WRC == STATUS_SUCCESS) {
stop_waiter:
// if(!
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);//) {
/* delay.QuadPart = -1000000; // 0.1 sec
KeDelayExecutionThread(KernelMode, FALSE, &delay);
try_return(RC);*/
// }
Vcb->EjectWaiter = NULL;
UDFReleaseResource(&(Vcb->VCBResource));
KeSetEvent(WC->WaiterStopped, 0, FALSE);
MyFreePool__(WC);
WC = NULL;
UDFPrint((" UDFEjectReqWaiter: exit 3\n"));
return;
}
BM_FlushPriod = Vcb->BM_FlushPriod;
Tree_FlushPriod = Vcb->Tree_FlushPriod;
// check if we approaching end of disk
if(space_check_counter > 2) {
// update FreeAllocUnits if it is necessary
if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) && Vcb->Modified) {
Vcb->FreeAllocUnits = UDFGetFreeSpace(Vcb);
}
// update LowFreeSpace flag
Vcb->LowFreeSpace = (Vcb->FreeAllocUnits < max(Vcb->FECharge,UDF_DEFAULT_FE_CHARGE)*128);
if(Vcb->LowFreeSpace && !OldLowFreeSpace) {
// initiate Flush process if we crossed LowFreeSpace boundary
Vcb->Tree_FlushTime = Tree_FlushPriod+1;
Vcb->VCBFlags &= ~UDF_VCB_SKIP_EJECT_CHECK;
}
OldLowFreeSpace = Vcb->LowFreeSpace;
space_check_counter = 0;
}
space_check_counter++;
if(Vcb->VCBFlags & UDF_VCB_SKIP_EJECT_CHECK) {
SkipCount++;
SkipEjectCount++;
SkipEject = (SkipEjectCount <= Vcb->SkipEjectCountLimit);
SkipFlush = (SkipEjectCount <= Vcb->SkipCountLimit);
if(SkipEject || SkipFlush) {
Vcb->VCBFlags &= ~UDF_VCB_SKIP_EJECT_CHECK;
}
} else {
SkipEject = FALSE;
SkipFlush = FALSE;
}
if(WC->SoftEjectReq) {
SkipEject = FALSE;
SkipFlush = FALSE;
}
if(SkipFlush) {
Vcb->BM_FlushTime =
Vcb->Tree_FlushTime = 0;
} else {
SkipCount = 0;
}
if(!SkipEject) {
SkipEjectCount = 0;
}
if(SkipEject && SkipFlush) {
wait_eject:
delay.QuadPart = -10000000; // 1.0 sec
WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
if(WRC == STATUS_SUCCESS) {
goto stop_waiter;
}
try_return(RC);
}
if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED) {
goto wait_eject;
}
// check if the door is still locked
if(!SkipEject &&
(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) &&
(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER)) {
UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
RC = UDFPhSendIOCTL( IOCTL_CDRW_GET_CAPABILITIES,
TargetDevObj,
NULL,0,
&(WC->DevCap),sizeof(GET_CAPABILITIES_USER_OUT),
FALSE,NULL);
Error = &(WC->Error);
UDFPhSendIOCTL( IOCTL_CDRW_GET_LAST_ERROR, Vcb->TargetDeviceObject,
NULL,0,
Error,sizeof(GET_LAST_ERROR_USER_OUT),
TRUE,NULL);
UDFReleaseResource(&(Vcb->IoResource));
UDFPrint(("SK=%x ASC=%x, ASCQ=%x, IE=%x\n",
Error->SenseKey, Error->AdditionalSenseCode, Error->AdditionalSenseCodeQualifier, Error->LastError));
// check for Long Write In Progress
if( ((Error->SenseKey == SCSI_SENSE_NOT_READY) &&
(Error->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
(Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS ||
Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)) ) {
if((!Vcb->Modified &&
!(Vcb->VCBFlags & UDF_VCB_LAST_WRITE))
||
(Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL)) {
// we should forget about this disk...
UDFPrint((" LAST_WRITE %x\n", !!(Vcb->VCBFlags & UDF_VCB_LAST_WRITE)));
UDFPrint((" UDF_VCB_FLAGS_UNSAFE_IOCTL %x\n", !!(Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL)));
UDFPrint((" UDFEjectReqWaiter: Unexpected write-in-progress on !Modified volume\n"));
//ASSERT(FALSE);
Vcb->ForgetVolume = TRUE;
Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY | UDF_VCB_FLAGS_MEDIA_READ_ONLY;
MediaLoss = TRUE;
goto device_failure;
}
}
if( OS_SUCCESS(RC) &&
(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) &&
!(WC->DevCap.Capabilities2 & DevCap_lock_state)) {
// probably bus reset or power failure occured
// re-lock tray
UDFPrint((" UDFEjectReqWaiter: Unexpected tray unlock encountered. Try to re-lock\n"));
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
VcbAcquired = TRUE;
/* UDFResetDeviceDriver(Vcb, TargetDevObj, FALSE);
delay.QuadPart = -1000000; // 0.1 sec
KeDelayExecutionThread(KernelMode, FALSE, &delay);*/
// lock it
((PPREVENT_MEDIA_REMOVAL_USER_IN)(&(WC->DevCap)))->PreventMediaRemoval = TRUE;
UDFPhSendIOCTL( IOCTL_STORAGE_MEDIA_REMOVAL,
TargetDevObj,
&(WC->DevCap),sizeof(PREVENT_MEDIA_REMOVAL_USER_IN),
NULL,0,
FALSE,NULL);
delay.QuadPart = -1000000; // 0.1 sec
KeDelayExecutionThread(KernelMode, FALSE, &delay);
// force write mode re-initialization
Vcb->LastModifiedTrack = 0;
// try_return(RC);
}
}
UDFVVerify(Vcb, 0 /* partial verify */);
if(!SkipFlush &&
!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
(BM_FlushPriod || Tree_FlushPriod)) {
KeQuerySystemTime(&delay);
d = (uint32)((delay.QuadPart - time.QuadPart) / 10000000);
time = delay;
Vcb->BM_FlushTime += d;
Vcb->Tree_FlushTime += d;
if(!Vcb->CDR_Mode) {
AllFlushed = FALSE;
UDFPrint((" SkipCount=%x, SkipCountLimit=%x\n",
SkipCount,
Vcb->SkipCountLimit));
if( Tree_FlushPriod &&
(Tree_FlushPriod < Vcb->Tree_FlushTime)) {
UDFPrint((" Tree_FlushPriod %I64x, Vcb->Tree_FlushTime %I64x\n",
Tree_FlushPriod,
Vcb->Tree_FlushTime));
// do not touch unchanged volume
if((Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) ||
!Vcb->Modified)
goto skip_BM_flush;
// Acquire Vcb resource
if(!VcbAcquired) {
if(!UDFAcquireResourceExclusive(&(Vcb->VCBResource), FALSE)) {
delay.QuadPart = -10000000; // 1.0 sec
WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
if(WRC == STATUS_SUCCESS) {
goto stop_waiter;
}
try_return(RC);
}
VcbAcquired = TRUE;
}
UDFPrint(("UDF: Flushing Directory Tree....\n"));
if( BM_FlushPriod &&
(BM_FlushPriod < Vcb->BM_FlushTime)) {
UDFPrint((" full flush\n"));
flush_stat = UDFFlushADirectory(Vcb, Vcb->RootDirFCB->FileInfo, &IoStatus, UDF_FLUSH_FLAGS_BREAKABLE);
} else {
UDFPrint((" light flush\n"));
flush_stat = UDFFlushADirectory(Vcb, Vcb->RootDirFCB->FileInfo, &IoStatus, UDF_FLUSH_FLAGS_BREAKABLE | UDF_FLUSH_FLAGS_LITE);
}
if(flush_stat & UDF_FLUSH_FLAGS_INTERRUPTED)
try_return(RC);
FlushWCache = TRUE;
UDFVVerify(Vcb, UFD_VERIFY_FLAG_BG /* partial verify */);
//UDFVFlush(Vcb);
skip_BM_flush:
Vcb->Tree_FlushTime = 0;
}
if( BM_FlushPriod &&
(BM_FlushPriod < Vcb->BM_FlushTime)) {
UDFPrint((" BM_FlushPriod %I64x, Vcb->BM_FlushTime %I64x\n",
BM_FlushPriod,
Vcb->BM_FlushTime));
// do not touch unchanged volume
if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY)
goto skip_BM_flush2;
if(!Vcb->Modified)
goto skip_BM_flush2;
if(!VcbAcquired) {
if(!UDFAcquireResourceExclusive(&(Vcb->VCBResource), FlushWCache /*|| FALSE*/)) {
delay.QuadPart = -10000000; // 1.0 sec
WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
if(WRC == STATUS_SUCCESS) {
goto stop_waiter;
}
try_return(RC);
}
VcbAcquired = TRUE;
}
UDFAcquireResourceExclusive(&(Vcb->BitMapResource1),TRUE);
// UDF_CHECK_BITMAP_RESOURCE(Vcb);
if(Vcb->FSBM_ByteCount != RtlCompareMemory(Vcb->FSBM_Bitmap, Vcb->FSBM_OldBitmap, Vcb->FSBM_ByteCount)) {
flags |= 1;
}
/* if(FlushWCache) {
AllFlushed = TRUE;
}*/
AllFlushed =
FlushWCache = TRUE;
#ifndef UDF_READ_ONLY_BUILD
UDFFlushAllCachedAllocations(Vcb, UDF_PREALLOC_CLASS_FE);
UDFFlushAllCachedAllocations(Vcb, UDF_PREALLOC_CLASS_DIR);
UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr, &(Vcb->VolIdent));
UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr2, &(Vcb->VolIdent));
if(Vcb->VerifyOnWrite) {
UDFPrint(("UDF: Flushing cache for verify\n"));
//WCacheFlushAll__(&(Vcb->FastCache), Vcb);
WCacheFlushBlocks__(&(Vcb->FastCache), Vcb, 0, Vcb->LastLBA);
UDFVFlush(Vcb);
}
#ifdef UDF_DBG
UDFPrint(("UDF: Flushing Free Space Bitmap....\n"));
// if(!OS_SUCCESS(UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr, &(Vcb->VolIdent))))
// UDFPrint(("Error updating VolIdent\n"));
if(!OS_SUCCESS(UDFUpdateVDS(Vcb, Vcb->VDS1, Vcb->VDS1 + Vcb->VDS1_Len, flags)))
UDFPrint(("Error updating Main VDS\n"));
if(!OS_SUCCESS(UDFUpdateVDS(Vcb, Vcb->VDS2, Vcb->VDS2 + Vcb->VDS2_Len, flags)))
UDFPrint(("Error updating Reserve VDS\n"));
#else
UDFUpdateVDS(Vcb, Vcb->VDS1, Vcb->VDS1 + Vcb->VDS1_Len, flags);
UDFUpdateVDS(Vcb, Vcb->VDS2, Vcb->VDS2 + Vcb->VDS2_Len, flags);
#endif // UDF_DBG
// Update Integrity Desc if any
if(Vcb->LVid && Vcb->origIntegrityType == INTEGRITY_TYPE_CLOSE) {
UDFUpdateLogicalVolInt(Vcb, TRUE);
}
if(flags & 1) {
RtlCopyMemory(Vcb->FSBM_OldBitmap, Vcb->FSBM_Bitmap, Vcb->FSBM_ByteCount);
}
#endif //UDF_READ_ONLY_BUILD
UDFPreClrModified(Vcb);
UDFReleaseResource(&(Vcb->BitMapResource1));
skip_BM_flush2:
Vcb->BM_FlushTime = 0;
}
if(FlushWCache) {
FlushWCache = FALSE;
WCacheFlushAll__(&(Vcb->FastCache), Vcb);
}
if(AllFlushed) {
//Vcb->Modified = FALSE;
UDFClrModified(Vcb);
}
if(VcbAcquired) {
VcbAcquired = FALSE;
UDFReleaseResource(&(Vcb->VCBResource));
}
if(!Vcb->Tree_FlushTime &&
!Vcb->BM_FlushTime)
SkipCount = 0;
}
} else {
//SkipCount = 0;
}
if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA))
try_return(RC);
UDFPrint((" UDFEjectReqWaiter: check removable media\n"));
if(!WC->SoftEjectReq && SkipEject) {
try_return(RC);
}
if(!WC->SoftEjectReq) {
if(!UseEvent) {
UDFPrint((" Eject Button ignored\n"));
try_return(RC);
}
/* if( (Vcb->VCBFlags & UDF_VCB_LAST_WRITE) &&
!(Vcb->VCBFlags & UDF_VCB_FLAGS_NO_SYNC_CACHE) ){
// delay.QuadPart = -100000; // 0.01 sec
// KeDelayExecutionThread(KernelMode, FALSE, &delay);
OSSTATUS RC;
UDFPrint((" Sync cache before GET_EVENT\n"));
RC = UDFSyncCache(Vcb);
if(RC == STATUS_INVALID_DEVICE_REQUEST) {
Vcb->VCBFlags |= UDF_VCB_FLAGS_NO_SYNC_CACHE;
}
// delay.QuadPart = -300000; // 0.03 sec
// KeDelayExecutionThread(KernelMode, FALSE, &delay);
Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
}*/
ASSERT(sizeof(TEST_UNIT_READY_USER_OUT) <= sizeof(GET_EVENT_USER_OUT));
RC = UDFTSendIOCTL( IOCTL_CDRW_TEST_UNIT_READY,
Vcb,
NULL,0,
&(WC->EjectReqBuffer),sizeof(TEST_UNIT_READY_USER_OUT),
FALSE,NULL);
if(RC != STATUS_SUCCESS &&
RC != STATUS_DATA_OVERRUN) {
if(RC == STATUS_NO_SUCH_DEVICE) {
UDFPrint((" Device loss\n"));
goto device_failure;
}
if(RC == STATUS_NO_MEDIA_IN_DEVICE) {
UDFPrint((" Media loss\n"));
goto media_loss;
}
}
UDFPrint((" Reading events...\n"));
if(supported_evt_classes) {
for(i=1; i<=EventRetStat_Class_Mask;i++) {
evt_type = (((UCHAR)1) << i);
if( !(supported_evt_classes & evt_type) )
continue;
/*
if( evt_type == EventStat_Class_Media )
continue;
if( evt_type == EventStat_Class_ExternalReq )
continue;
*/
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = evt_type;
RC = UDFTSendIOCTL( IOCTL_CDRW_GET_EVENT,
Vcb,
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
FALSE,NULL);
if(RC == STATUS_INVALID_DEVICE_REQUEST) {
supported_evt_classes &= ~evt_type;
continue;
}
if(RC == STATUS_NO_SUCH_DEVICE) {
UDFPrint((" Device loss (2)\n"));
goto device_failure;
}
if(!OS_SUCCESS(RC)) {
continue;
}
if(WC->EjectReqBuffer.MediaChange.Header.Flags.Flags & EventRetStat_NEA) {
continue;
}
if( evt_type == EventStat_Class_Media ) {
UDFPrint((" EventStat_Class_Media:\n"));
if((WC->EjectReqBuffer.MediaChange.Header.Flags.Flags & EventRetStat_Class_Mask) !=
EventRetStat_Class_Media) {
continue;
}
retry_media_presence_check:
if(!(WC->EjectReqBuffer.MediaChange.Byte1.Flags & EventStat_MediaStat_Present) ||
(WC->EjectReqBuffer.MediaChange.Byte1.Flags & EventStat_MediaStat_DoorOpen)) {
// something wrong....
RC = UDFTSendIOCTL( IOCTL_CDRW_TEST_UNIT_READY,
Vcb,
NULL,0,
&(WC->EjectReqBuffer),sizeof(TEST_UNIT_READY_USER_OUT),
FALSE,NULL);
if(RC == STATUS_SUCCESS ||
RC == STATUS_DATA_OVERRUN) {
UDFPrint((" Buggy GET_EVENT media presence flag %x\n",
WC->EjectReqBuffer.MediaChange.Byte1));
WC->EjectReqBuffer.MediaChange.Byte1.Flags |= EventStat_MediaStat_Present;
WC->EjectReqBuffer.MediaChange.Byte1.Flags &= ~EventStat_MediaStat_DoorOpen;
goto retry_media_presence_check;
}
media_loss:
UDFPrint((" Unexpected media loss. Check device status\n"));
UseEject = FALSE;
MediaLoss = TRUE;
} else
// check if eject request occured
if( (WC->EjectReqBuffer.MediaChange.Byte0.Flags & EventStat_MediaEvent_Mask) !=
EventStat_MediaEvent_EjectReq ) {
continue;
}
UDFPrint((" eject requested\n"));
WC->SoftEjectReq = TRUE;
break;
}
if( evt_type == EventStat_Class_ExternalReq ) {
UDFPrint((" EventStat_Class_ExternalReq:\n"));
if((WC->EjectReqBuffer.ExternalReq.Header.Flags.Flags & EventRetStat_Class_Mask) !=
EventRetStat_Class_ExternReq)
continue;
switch(WC->EjectReqBuffer.ExternalReq.Byte0.Flags & EventStat_ExtrnReqEvent_Mask) {
case EventStat_ExtrnReqEvent_KeyDown:
case EventStat_ExtrnReqEvent_KeyUp:
case EventStat_ExtrnReqEvent_ExtrnReq:
UDFPrint((" eject requested (%x)\n", WC->EjectReqBuffer.ExternalReq.Byte0.Flags));
WC->SoftEjectReq = TRUE;
break;
}
continue;
}
}
if(!supported_evt_classes) {
UseEvent = FALSE;
}
if(!WC->SoftEjectReq) {
try_return(RC);
}
} else {
UDFPrint((" Reading Media Event...\n"));
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = EventStat_Class_Media;
RC = UDFTSendIOCTL( IOCTL_CDRW_GET_EVENT,
Vcb,
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
FALSE,NULL);
if(!OS_SUCCESS(RC)) {
if(RC == STATUS_NO_SUCH_DEVICE)
goto device_failure;
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = EventStat_Class_ExternalReq;
RC = UDFTSendIOCTL( IOCTL_CDRW_GET_EVENT,
Vcb,
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
&(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
FALSE,NULL);
if(RC == STATUS_NO_SUCH_DEVICE)
goto device_failure;
if(RC == STATUS_INVALID_DEVICE_REQUEST) {
UseEvent = FALSE;
}
try_return(RC);
}
supported_evt_classes = WC->EjectReqBuffer.MediaChange.Header.SupportedClasses;
try_return(RC);
}
}
// FlushAndEject = TRUE;
device_failure:
// Ok. Lets flush all we have in memory, dismount volume & eject disc
// Acquire Vcb resource
Vcb->SoftEjectReq = TRUE;
UDFPrint((" UDFEjectReqWaiter: ejecting...\n"));
#ifdef UDF_DELAYED_CLOSE
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
UDFPrint((" UDFEjectReqWaiter: set UDF_VCB_FLAGS_NO_DELAYED_CLOSE\n"));
Vcb->VCBFlags |= UDF_VCB_FLAGS_NO_DELAYED_CLOSE;
UDFReleaseResource(&(Vcb->VCBResource));
#endif //UDF_DELAYED_CLOSE
UDFPrint((" UDFEjectReqWaiter: UDFCloseAllSystemDelayedInDir\n"));
RC = UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo);
ASSERT(OS_SUCCESS(RC));
#ifdef UDF_DELAYED_CLOSE
UDFPrint((" UDFEjectReqWaiter: UDFCloseAllDelayed\n"));
UDFCloseAllDelayed(Vcb);
//ASSERT(OS_SUCCESS(RC));
#endif //UDF_DELAYED_CLOSE
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
UDFPrint((" UDFEjectReqWaiter: UDFDoDismountSequence\n"));
UDFDoDismountSequence(Vcb, (PPREVENT_MEDIA_REMOVAL_USER_IN)&(WC->EjectReqBuffer), UseEject);
if (MediaLoss) {
Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED;
Vcb->WriteSecurity = FALSE;
}
Vcb->EjectWaiter = NULL;
Vcb->SoftEjectReq = FALSE;
UDFReleaseResource(&(Vcb->VCBResource));
UDFPrint((" UDFEjectReqWaiter: set WaiterStopped\n"));
KeSetEvent(WC->WaiterStopped, 0, FALSE);
MyFreePool__(WC);
WC = NULL;
UDFPrint((" UDFEjectReqWaiter: exit 1\n"));
return;
try_exit: NOTHING;
} _SEH2_FINALLY {
if(VcbAcquired) {
VcbAcquired = FALSE;
UDFReleaseResource(&(Vcb->VCBResource));
}
/* if(WC) {
delay.QuadPart = -10000000; // 1.0 sec
WRC = KeWaitForSingleObject(&(Vcb->WaiterStopped), Executive, KernelMode, FALSE, &delay);
if(WRC == STATUS_SUCCESS) {
goto stop_waiter;
}
}*/
} _SEH2_END;
}
// Simply make compiler happy
return;
} // end UDFEjectReqWaiter()
void
UDFStopEjectWaiter(PVCB Vcb) {
UDFPrint((" UDFStopEjectWaiter: try\n"));
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
_SEH2_TRY {
if(Vcb->EjectWaiter) {
UDFPrint((" UDFStopEjectWaiter: set flag\n"));
KeSetEvent( &(Vcb->EjectWaiter->StopReq), 0, FALSE );
} else {
// return;
}
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
BrutePoint();
} _SEH2_END;
UDFReleaseResource(&(Vcb->VCBResource));
_SEH2_TRY {
if(Vcb->VCBFlags & UDF_VCB_FLAGS_STOP_WAITER_EVENT) {
UDFPrint((" UDFStopEjectWaiter: wait\n"));
KeWaitForSingleObject(&(Vcb->WaiterStopped), Executive, KernelMode, FALSE, NULL);
}
Vcb->VCBFlags &= ~UDF_VCB_FLAGS_STOP_WAITER_EVENT;
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
BrutePoint();
} _SEH2_END;
ASSERT(!Vcb->EjectWaiter);
UDFPrint((" UDFStopEjectWaiter: exit\n"));
} // end UDFStopEjectWaiter()
OSSTATUS
UDFDoDismountSequence(
IN PVCB Vcb,
IN PPREVENT_MEDIA_REMOVAL_USER_IN Buf,
IN BOOLEAN Eject
)
{
LARGE_INTEGER delay;
// OSSTATUS RC;
ULONG i;
// flush system cache
UDFFlushLogicalVolume(NULL, NULL, Vcb, 0);
UDFPrint(("UDFDoDismountSequence:\n"));
delay.QuadPart = -1000000; // 0.1 sec
KeDelayExecutionThread(KernelMode, FALSE, &delay);
// wait for completion of all backgroung writes
while(Vcb->BGWriters) {
delay.QuadPart = -5000000; // 0.5 sec
KeDelayExecutionThread(KernelMode, FALSE, &delay);
}
// release WCache
WCacheRelease__(&(Vcb->FastCache));
UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
// unlock media, drop our own Locks
if(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) {
UDFPrint((" cleanup tray-lock (%d+2):\n", Vcb->MediaLockCount));
for(i=0; i<Vcb->MediaLockCount+2; i++) {
Buf->PreventMediaRemoval = FALSE;
UDFPhSendIOCTL(IOCTL_STORAGE_MEDIA_REMOVAL,
Vcb->TargetDeviceObject,
Buf,sizeof(PREVENT_MEDIA_REMOVAL_USER_IN),
NULL,0,
FALSE,NULL);
KeDelayExecutionThread(KernelMode, FALSE, &delay);
}
delay.QuadPart = -2000000; // 0.2 sec
}
if(!Vcb->ForgetVolume) {
if(!UDFIsDvdMedia(Vcb)) {
// send speed limits to drive
UDFPrint((" Restore drive speed on dismount\n"));
Vcb->SpeedBuf.ReadSpeed = Vcb->MaxReadSpeed;
Vcb->SpeedBuf.WriteSpeed = Vcb->MaxWriteSpeed;
UDFPhSendIOCTL(IOCTL_CDRW_SET_SPEED,
Vcb->TargetDeviceObject,
&(Vcb->SpeedBuf),sizeof(SET_CD_SPEED_USER_IN),
NULL,0,TRUE,NULL);
}
if(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER) {
CLOSE_TRK_SES_USER_IN CBuff;
// reset driver
UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, TRUE);
delay.QuadPart = -2000000; // 0.2 sec
KeDelayExecutionThread(KernelMode, FALSE, &delay);
memset(&CBuff,0,sizeof(CLOSE_TRK_SES_USER_IN));
// stop BG format
if(Vcb->MRWStatus) {
UDFPrint((" Stop background formatting\n"));
CBuff.Byte1.Flags = 0;//CloseTrkSes_Immed;
CBuff.Byte2.Flags = CloseTrkSes_Ses;
CBuff.TrackNum = 1;
UDFPhSendIOCTL(IOCTL_CDRW_CLOSE_TRK_SES,
Vcb->TargetDeviceObject,
&CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
&CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
FALSE, NULL );
/* } else
if(Vcb->MediaClassEx == CdMediaClass_DVDRW) {
UDFPrint((" Close BG-formatted track\n"));
CBuff.Byte1.Flags = 0;//CloseTrkSes_Immed;
CBuff.Byte2.Flags = CloseTrkSes_Trk;
CBuff.TrackNum = 1;
RC = UDFPhSendIOCTL(IOCTL_CDRW_CLOSE_TRK_SES,
Vcb->TargetDeviceObject,
&CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
&CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
FALSE, NULL );
*/
}
// reset driver
UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, TRUE);
delay.QuadPart = -1000000; // 0.1 sec
KeDelayExecutionThread(KernelMode, FALSE, &delay);
}
// eject media
if(Eject &&
(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA)) {
UDFPhSendIOCTL(IOCTL_STORAGE_EJECT_MEDIA,
Vcb->TargetDeviceObject,
NULL,0,
NULL,0,
FALSE,NULL);
}
// notify media change
/* if(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER) {
((PNOTIFY_MEDIA_CHANGE_USER_IN)Buf)->Autorun = FALSE;
RC = UDFPhSendIOCTL(IOCTL_CDRW_NOTIFY_MEDIA_CHANGE,
Vcb->TargetDeviceObject,
Buf,sizeof(NOTIFY_MEDIA_CHANGE_USER_IN),
NULL,0,
FALSE,NULL);
}*/
}
UDFReleaseResource(&(Vcb->IoResource));
// unregister shutdown notification
if(Vcb->ShutdownRegistered) {
IoUnregisterShutdownNotification(Vcb->VCBDeviceObject);
Vcb->ShutdownRegistered = FALSE;
}
// allow media change checks (this will lead to dismount)
// ... and make it Read-Only... :-\~
Vcb->VCBFlags &= ~UDF_VCB_FLAGS_MEDIA_LOCKED;
// Return back XP CD Burner Volume
/*
if (Vcb->CDBurnerVolumeValid) {
RtlWriteRegistryValue(RTL_REGISTRY_USER | RTL_REGISTRY_OPTIONAL,
REG_CD_BURNER_KEY_NAME,REG_CD_BURNER_VOLUME_NAME,
REG_SZ,(PVOID)&(Vcb->CDBurnerVolume),sizeof(Vcb->CDBurnerVolume));
ExFreePool(Vcb->CDBurnerVolume.Buffer);
}
*/
UDFPrint((" set UnsafeIoctl\n"));
Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL;
return STATUS_SUCCESS;
} // end UDFDoDismountSequence()