mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
844 lines
35 KiB
C++
844 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()
|
|
|