windows-nt/Source/XPSP1/NT/base/fs/hsm/rms/server/rmsdrive.cpp

1363 lines
31 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
<EFBFBD> 1998 Seagate Software, Inc. All rights reserved
Module Name:
RmsDrive.cpp
Abstract:
Implementation of CRmsDrive
Author:
Brian Dodd [brian] 15-Nov-1996
Revision History:
--*/
#include "stdafx.h"
#include "RmsDrive.h"
#include "RmsServr.h"
int CRmsDrive::s_InstanceCount = 0;
#define RMS_CRITICAL_SECTION 1
////////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP
CRmsDrive::CompareTo(
IN IUnknown *pCollectable,
OUT SHORT *pResult
)
/*++
Implements:
IWsbCollectable::CompareTo
--*/
{
HRESULT hr = E_FAIL;
SHORT result = 1;
WsbTraceIn( OLESTR("CRmsDrive::CompareTo"), OLESTR("") );
try {
// Validate arguments - Okay if pResult is NULL
WsbAssertPointer( pCollectable );
// !!!!!
//
// IMPORTANT: The collectable coming in may not be a CRmsDrive if the collection
// is the unconfigured device list.
//
// !!!!!
CComQIPtr<IRmsComObject, &IID_IRmsComObject> pObject = pCollectable;
// Every collectable should be an CRmsComObject
WsbAssertPointer( pObject );
switch ( m_findBy ) {
case RmsFindByDeviceInfo:
case RmsFindByDeviceAddress:
case RmsFindByDeviceName:
case RmsFindByDeviceType:
// Do CompareTo for device
hr = CRmsDevice::CompareTo( pCollectable, &result );
break;
case RmsFindByElementNumber:
case RmsFindByMediaSupported:
// Do CompareTo for changer element
hr = CRmsChangerElement::CompareTo( pCollectable, &result );
break;
case RmsFindByObjectId:
default:
// Do CompareTo for object
hr = CRmsComObject::CompareTo( pCollectable, &result );
break;
}
}
WsbCatch( hr );
if ( SUCCEEDED(hr) && (0 != pResult) ){
*pResult = result;
}
WsbTraceOut( OLESTR("CRmsDrive::CompareTo"),
OLESTR("hr = <%ls>, result = <%ls>"),
WsbHrAsString( hr ), WsbPtrToShortAsString( pResult ) );
return hr;
}
STDMETHODIMP
CRmsDrive::FinalConstruct(
void
)
/*++
Implements:
CComObjectRoot::FinalConstruct
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::FinalConstruct"), OLESTR(""));
try {
WsbAssertHr(CWsbObject::FinalConstruct());
// Initialize values
m_MountReference = 0;
m_UnloadNowTime.dwHighDateTime = 0;
m_UnloadNowTime.dwLowDateTime = 0;
m_UnloadThreadHandle = NULL;
try {
// May raise STATUS_NO_MEMORY exception
InitializeCriticalSection(&m_CriticalSection);
} catch(DWORD status) {
WsbLogEvent(status, 0, NULL, NULL);
switch (status) {
case STATUS_NO_MEMORY:
WsbThrow(E_OUTOFMEMORY);
break;
default:
WsbThrow(E_UNEXPECTED);
break;
}
}
m_UnloadNowEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_UnloadedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
} WsbCatch(hr);
s_InstanceCount++;
WsbTraceAlways(OLESTR("CRmsDrive::s_InstanceCount += %d\n"), s_InstanceCount);
WsbTraceOut(OLESTR("CRmsDrive::FinalConstruct"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
STDMETHODIMP
CRmsDrive::FinalRelease(void)
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::FinalRelease"), OLESTR(""));
try {
try {
// InitializeCriticalSection raises an exception. Delete may too.
DeleteCriticalSection(&m_CriticalSection);
} catch(DWORD status) {
WsbLogEvent(status, 0, NULL, NULL);
}
CloseHandle(m_UnloadNowEvent);
CloseHandle(m_UnloadedEvent);
CWsbObject::FinalRelease();
} WsbCatch(hr);
s_InstanceCount--;
WsbTraceAlways(OLESTR("CRmsDrive::s_InstanceCount -= %d\n"), s_InstanceCount);
WsbTraceOut(OLESTR("CRmsDrive::FinalRelease"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
STDMETHODIMP
CRmsDrive::GetClassID(
OUT CLSID* pClsid
)
/*++
Implements:
IPersist::GetClassId
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::GetClassID"), OLESTR(""));
try {
WsbAssert(0 != pClsid, E_POINTER);
*pClsid = CLSID_CRmsDrive;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::GetClassID"), OLESTR("hr = <%ls>, CLSID = <%ls>"), WsbHrAsString(hr), WsbGuidAsString(*pClsid));
return(hr);
}
STDMETHODIMP
CRmsDrive::GetSizeMax(
OUT ULARGE_INTEGER* pcbSize
)
/*++
Implements:
IPersistStream::GetSizeMax
--*/
{
HRESULT hr = E_NOTIMPL;
WsbTraceIn(OLESTR("CRmsDrive::GetSizeMax"), OLESTR(""));
// try {
// WsbAssert(0 != pcbSize, E_POINTER);
// // Set up max size
// pcbSize->QuadPart = WsbPersistSizeOf(LONG); // m_MountReference
// } WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::GetSizeMax"), OLESTR("hr = <%ls>, Size = <%ls>"), WsbHrAsString(hr), WsbPtrToUliAsString(pcbSize));
return(hr);
}
STDMETHODIMP
CRmsDrive::Load(
IN IStream* pStream
)
/*++
Implements:
IPersistStream::Load
--*/
{
HRESULT hr = S_OK;
ULONG ulBytes = 0;
WsbTraceIn(OLESTR("CRmsDrive::Load"), OLESTR(""));
try {
WsbAssert(0 != pStream, E_POINTER);
WsbAffirmHr(CRmsDevice::Load(pStream));
// Read value
WsbAffirmHr(WsbLoadFromStream(pStream, &m_MountReference));
// We just reset to zero, one day we could try to reconnect to
// the process that issued the mount...
m_MountReference = 0;
}
WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::Load"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
STDMETHODIMP
CRmsDrive::Save(
IN IStream* pStream,
IN BOOL clearDirty
)
/*++
Implements:
IPersistStream::Save
--*/
{
HRESULT hr = S_OK;
ULONG ulBytes = 0;
WsbTraceIn(OLESTR("CRmsDrive::Save"), OLESTR("clearDirty = <%ls>"), WsbBoolAsString(clearDirty));
try {
WsbAssert(0 != pStream, E_POINTER);
WsbAffirmHr(CRmsDevice::Save(pStream, clearDirty));
// Write value
WsbAffirmHr(WsbSaveToStream(pStream, m_MountReference));
// Do we need to clear the dirty bit?
if (clearDirty) {
m_isDirty = FALSE;
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::Save"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
STDMETHODIMP
CRmsDrive::Test(
OUT USHORT *pPassed,
OUT USHORT *pFailed
)
/*++
Implements:
IWsbTestable::Test
--*/
{
HRESULT hr = S_OK;
CComPtr<IRmsDrive> pDrive1;
CComPtr<IRmsDrive> pDrive2;
CComPtr<IPersistFile> pFile1;
CComPtr<IPersistFile> pFile2;
LONG i;
LONG longWork1;
WsbTraceIn(OLESTR("CRmsDrive::Test"), OLESTR(""));
try {
// Get the Drive interface.
hr = S_OK;
try {
WsbAssertHr(((IUnknown*) (IRmsDrive*) this)->QueryInterface(IID_IRmsDrive, (void**) &pDrive1));
// Test All of MountReference Functions
ResetMountReference();
GetMountReference(&longWork1);
if(longWork1 == 0) {
(*pPassed)++;
} else {
(*pFailed)++;
}
for(i = 1; i < 20; i++){
AddMountReference();
GetMountReference(&longWork1);
if(longWork1 == i){
(*pPassed)++;
} else {
(*pFailed)++;
}
}
for(i = 19; i == 0; i--){
ReleaseMountReference();
GetMountReference(&longWork1);
if(longWork1 == i){
(*pPassed)++;
} else {
(*pFailed)++;
}
}
} WsbCatch(hr);
// Tally up the results
hr = S_OK;
if (*pFailed) {
hr = S_FALSE;
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::Test"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
STDMETHODIMP
CRmsDrive::GetMountReference(
OUT LONG *pRefs
)
/*++
Implements:
IRmsDrive::GetMountReference
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::GetMountReference"), OLESTR(""));
LONG refs = -999;
try {
WsbAssertPointer(pRefs);
refs = m_MountReference;
*pRefs = refs;
} WsbCatch(hr)
WsbTraceOut(OLESTR("CRmsDrive::GetMountReference"), OLESTR("hr = <%ls>, refs = %d"),
WsbHrAsString(hr), refs);
return hr;
}
STDMETHODIMP
CRmsDrive::ResetMountReference(
void
)
/*++
Implements:
IRmsDrive::ResetMountReference
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::ResetMountReference"), OLESTR(""));
#if RMS_CRITICAL_SECTION
try {
// <<<<< ENTER SINGLE THREADED SECTION
WsbAffirmHr(Lock());
m_MountReference = 0;
m_isDirty = TRUE;
WsbAffirmHr(Unlock());
// >>>>> LEAVE SINGLE THREADED SECTION
} WsbCatch(hr)
#else
InterlockedExchange( &m_MountReference, 0);
m_isDirty = TRUE;
#endif
WsbTraceOut(OLESTR("CRmsDrive::ResetMountReference"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CRmsDrive::AddMountReference(
void
)
/*++
Implements:
IRmsDrive::AddMountReference
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::AddMountReference"), OLESTR(""));
LONG refs = -999;
#if RMS_CRITICAL_SECTION
try {
// <<<<< ENTER SINGLE THREADED SECTION
WsbAffirmHr(Lock());
m_MountReference++;
m_isDirty = TRUE;
refs = m_MountReference;
WsbAffirmStatus(ResetEvent(m_UnloadedEvent));
WsbAffirmHr(Unlock());
// >>>>> LEAVE SINGLE THREADED SECTION
} WsbCatch(hr)
#else
refs = InterlockedIncrement( &m_MountReference );
m_isDirty = TRUE;
#endif
WsbTraceOut(OLESTR("CRmsDrive::AddMountReference"), OLESTR("hr = <%ls>, refs = %d"),
WsbHrAsString(hr), refs);
return hr;
}
STDMETHODIMP
CRmsDrive::ReleaseMountReference(
IN DWORD dwOptions
)
/*++
Implements:
IRmsDrive::ReleaseMountReference
--*/
{
HRESULT hr S_OK;
WsbTraceIn(OLESTR("CRmsDrive::ReleaseMountReference"), OLESTR("<%ld>"), dwOptions);
// We need to be sure this object doesn't go away until we're done.
// This happens when we dismount a NTMS managed cartridge.
CComPtr<IRmsDrive> thisDrive = this;
LONG refs = -999;
BOOL bUnloadNow =
( (dwOptions & RMS_DISMOUNT_IMMEDIATE) || (dwOptions & RMS_DISMOUNT_DEFERRED_ONLY) ) ? TRUE : FALSE;
try {
#if RMS_CRITICAL_SECTION
// <<<<< ENTER SINGLE THREADED SECTION
WsbAffirmHr(Lock());
m_MountReference--;
m_isDirty = TRUE;
refs = m_MountReference;
#else
refs = InterlockedDecrement( &m_MountReference );
m_isDirty = TRUE;
#endif
// Note:
// Even if the caller requests immediate dismount, if the ref count is > 0,
// the media is not dismounted (only the ref count is decreased).
// This is necessary because positive ref count means that some other component
// is also using the media (possible for Optical). The media must be dismounted
// only when this other component is done as well.
if (refs < 0) {
//
// This shouldn't happen under normal conditions... if it does,
// we quiety reset the the reference count and try to recover.
//
WsbLogEvent(E_UNEXPECTED, sizeof(refs), &refs, NULL);
InterlockedExchange( &m_MountReference, 0);
refs = 0;
// If we don't have a cartridge in the drive, there's no point
// in continueing.
WsbAffirm(S_OK == IsOccupied(), E_ABORT);
}
if (0 == refs) {
//
// Deferred Dismount Logic: We wait the specified time before
// dismounting the media. Each dismount request resets the dismount
// now time. As long as the media is actively used, it will not be
// dismounted.
//
// Retrieve the DeferredDismountWaitTime parameter
DWORD size;
OLECHAR tmpString[256];
DWORD waitTime = RMS_DEFAULT_DISMOUNT_WAIT_TIME;
if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_DISMOUNT_WAIT_TIME, tmpString, 256, &size))) {
waitTime = wcstol(tmpString, NULL, 10);
WsbTrace(OLESTR("DismountWaitTime is %d milliseconds.\n"), waitTime);
}
if (waitTime > 0 && !bUnloadNow) {
// convert waitTime to 100-nanosecond units
waitTime *= 10000;
FILETIME now;
GetSystemTimeAsFileTime(&now);
ULARGE_INTEGER time;
time.LowPart = now.dwLowDateTime;
time.HighPart = now.dwHighDateTime;
time.QuadPart += waitTime;
m_UnloadNowTime.dwLowDateTime = time.LowPart;
m_UnloadNowTime.dwHighDateTime = time.HighPart;
WsbTrace(OLESTR("Target Unload Time = <%ls>\n"),
WsbQuickString(WsbFiletimeAsString(FALSE, m_UnloadNowTime)));
// If we already have an active unload thread we skip this step.
if (!m_UnloadThreadHandle) {
//
// Create a thread to wait for dismount
//
WsbTrace(OLESTR("Starting thread for deferred dismount.\n"));
DWORD threadId;
WsbAffirmHandle(m_UnloadThreadHandle = CreateThread(NULL, 1024, CRmsDrive::StartUnloadThread, this, 0, &threadId));
CloseHandle(m_UnloadThreadHandle);
}
}
else {
// Dismount the media now
// Double check that we still have something to dismount
if (S_OK == IsOccupied()) {
// Best effort - home
// Fixed drives are always occupied and we shouldn't call Home for their cartridge
FlushBuffers();
if (RmsDeviceFixedDisk != m_deviceType) {
if (S_OK == m_pCartridge->Home(dwOptions)) {
SetIsOccupied(FALSE);
}
}
}
// set event that blocks immediate unload
SetEvent(m_UnloadedEvent);
}
}
#if RMS_CRITICAL_SECTION
WsbAffirmHr(Unlock());
// >>>>> LEAVE SINGLE THREADED SECTION
} WsbCatchAndDo(hr,
WsbAffirmHr(Unlock());
// >>>>> LEAVE SINGLE THREADED SECTION
);
#else
} WsbCatch(hr)
#endif
WsbTraceOut(OLESTR("CRmsDrive::ReleaseMountReference"), OLESTR("hr = <%ls>, refs = %d"),
WsbHrAsString(hr), refs);
return hr;
}
STDMETHODIMP
CRmsDrive::SelectForMount(
void
)
/*++
Implements:
IRmsDrive::SelectForMount
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::SelectForMount"), OLESTR(""));
#if RMS_CRITICAL_SECTION
try {
// <<<<< ENTER SINGLE THREADED SECTION
WsbAffirmHr(Lock());
if (!m_MountReference) {
m_MountReference++;
m_isDirty = TRUE;
} else {
hr = RMS_E_DRIVE_BUSY;
}
WsbAffirmHr(Unlock());
// >>>>> LEAVE SINGLE THREADED SECTION
} WsbCatch(hr)
#else
LONG one = 1;
LONG zero = 0;
LONG flag = InterlockedCompareExchange( &m_MountReference, one, zero );
hr = ( flag > 0 ) ? RMS_E_DRIVE_BUSY : S_OK;
#endif
WsbTraceOut(OLESTR("CRmsDrive::SelectForMount"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CRmsDrive::CreateDataMover(
IDataMover **ptr)
/*++
Implements:
IRmsDrive::CreateDataMover
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::CreateDataMover"), OLESTR(""));
try {
WsbAssertPointer(ptr);
if (m_isOccupied) {
switch (m_mediaSupported) {
case RmsMedia8mm:
case RmsMedia4mm:
case RmsMediaDLT:
case RmsMediaTape:
{
//
// Create a tape style data mover to the drive
//
WsbAssertHr(CoCreateInstance(CLSID_CNtTapeIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)ptr));
}
break;
case RmsMediaWORM:
break;
case RmsMediaOptical:
case RmsMediaMO35:
case RmsMediaCDR:
case RmsMediaDVD:
case RmsMediaDisk:
case RmsMediaFixed:
{
//
// Create a file style data mover to the drive
//
WsbAssertHr(CoCreateInstance(CLSID_CNtFileIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)ptr));
}
break;
default:
WsbThrow(E_UNEXPECTED);
break;
}
}
else {
WsbThrow(RMS_E_RESOURCE_UNAVAILABLE);
}
// Initialize the data mover
WsbAffirmHr((*ptr)->SetDeviceName(m_deviceName));
WsbAffirmHr((*ptr)->SetCartridge(m_pCartridge));
// Update stroage info for this cartridge.
//
// IMPORTANT NOTE: This also needs to touch the physical device
// to make sure the device is ready for I/O.
// If we get device errors here, we must fail the
// mount.
CComQIPtr<IRmsStorageInfo, &IID_IRmsStorageInfo> pInfo = m_pCartridge;
// marking the FreeSpace to -1 gaurantees it's stale for the
// following GetLargestFreeSpace() call.
WsbAffirmHr(pInfo->SetFreeSpace(-1));
hr = (*ptr)->GetLargestFreeSpace(NULL, NULL);
if (MVR_E_UNRECOGNIZED_VOLUME == hr) {
// This is expected if this is an unformatted optical media
hr = S_OK;
}
WsbAffirmHr(hr);
WsbAssertHrOk(hr);
/*
Tracking DataMovers is only partially implemented.
CComQIPtr<IRmsServer, &IID_IRmsServer> pServer = g_pServer;
CComPtr<IWsbIndexedCollection> pDataMovers;
WsbAffirmHr(pServer->GetDataMovers(&pDataMovers));
WsbAffirmHr(pDataMovers->Add((IDataMover *)(*ptr)));
*/
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::CreateDataMover"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CRmsDrive::ReleaseDataMover(
IN IDataMover *ptr)
/*++
Implements:
IRmsDrive::ReleaseDataMover
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::ReleaseDataMover"), OLESTR(""));
try {
WsbAssertPointer(ptr);
WsbThrow(E_NOTIMPL);
/*
Tracking DataMovers is only partially implemented.
CComQIPtr<IRmsServer, &IID_IRmsServer> pServer = g_pServer;
CComPtr<IWsbIndexedCollection> pDataMovers;
WsbAffirmHr(pServer->GetDataMovers(&pDataMovers));
WsbAffirmHr(pDataMovers->RemoveAndRelease((IDataMover *)ptr));
ULONG activeDataMovers;
WsbAffirmHr(pDataMovers->GetEntries( &activeDataMovers));
WsbTrace(OLESTR("activeDataMovers = <%u>\n"), activeDataMovers);
*/
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::ReleaseDataMover"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CRmsDrive::Eject(
void
)
/*++
Implements:
IRmsDrive::Eject
--*/
{
HRESULT hr = E_FAIL;
WsbTraceIn(OLESTR("CRmsDrive::Eject"), OLESTR(""));
HANDLE hDrive = INVALID_HANDLE_VALUE;
try {
CWsbBstrPtr drive = "";
switch ( m_mediaSupported ) {
case RmsMedia8mm:
case RmsMedia4mm:
case RmsMediaDLT:
case RmsMediaTape:
drive = m_deviceName;
break;
case RmsMediaWORM:
break;
case RmsMediaOptical:
case RmsMediaMO35:
case RmsMediaCDR:
case RmsMediaDVD:
case RmsMediaDisk:
case RmsMediaFixed:
// TODO: permanently remove trailing \ from device name ????
WsbAffirmHr(drive.Realloc(2));
wcsncpy(drive, m_deviceName, 2);
drive.Prepend( OLESTR( "\\\\.\\" ) );
break;
}
int retry = 0;
do {
hDrive = CreateFile( drive,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
NULL
);
if ( INVALID_HANDLE_VALUE == hDrive )
Sleep(2000);
else
break;
} while ( retry++ < 10 );
WsbAssertHandle( hDrive );
DWORD dwReturn;
WsbAffirmHr(PrepareTape(hDrive, TAPE_UNLOAD, FALSE));
WsbAffirmHr(PrepareTape(hDrive, TAPE_UNLOCK, FALSE));
WsbAssertStatus( DeviceIoControl( hDrive,
IOCTL_STORAGE_EJECT_MEDIA,
NULL,
0,
NULL,
0,
&dwReturn,
NULL ));
WsbAssertStatus( CloseHandle( hDrive ) );
hr = S_OK;
}
WsbCatchAndDo( hr,
if ( INVALID_HANDLE_VALUE != hDrive )
CloseHandle( hDrive );
);
WsbTraceOut(OLESTR("CRmsDrive::Eject"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
STDMETHODIMP
CRmsDrive::GetLargestFreeSpace(
LONGLONG *pFreeSpace,
LONGLONG *pCapacity
)
/*++
Implements:
IRmsDrive::GetLargestFreeSpace
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::GetLargestFreeSpace"), OLESTR(""));
try {
CComPtr<IDataMover> pDataMover;
WsbAffirmHr(CreateDataMover(&pDataMover));
WsbAffirmHr(pDataMover->GetLargestFreeSpace(pFreeSpace, pCapacity));
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::GetLargestFreeSpace"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CRmsDrive::UnloadNow(void)
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::UnloadNow"), OLESTR(""));
try {
WsbAffirmHr(Lock());
WsbAffirmStatus(SetEvent(m_UnloadNowEvent));
WsbAffirmHr(Unlock());
switch(WaitForSingleObject(m_UnloadedEvent, INFINITE)) {
case WAIT_FAILED:
hr = HRESULT_FROM_WIN32(GetLastError());
WsbTrace(OLESTR("CRmsDrive::UnloadNow - Wait for Single Object returned error: %ls\n"),
WsbHrAsString(hr));
WsbAffirmHr(hr);
break;
case WAIT_TIMEOUT:
WsbTrace(OLESTR("CRmsDrive::UnloadNow - Awakened by timeout.\n"));
break;
default:
WsbTrace(OLESTR("CRmsDrive::UnloadNow - Awakened by external signal.\n"));
GetSystemTimeAsFileTime(&m_UnloadNowTime);
break;
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::UnloadNow"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
DWORD WINAPI
CRmsDrive::StartUnloadThread(
IN LPVOID pv)
{
return(((CRmsDrive*) pv)->Unload());
}
HRESULT
CRmsDrive::Unload(void)
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CRmsDrive::Unload"), OLESTR(""));
// We need to be sure this object doesn't go away until we're done.
// This happens when we dismount a NTMS managed cartridge.
CComPtr<IRmsDrive> thisDrive = this;
try {
BOOL waiting = TRUE;
LARGE_INTEGER delta = {0,0};
while (waiting) {
//
// !!!!! VERY IMPORTANT !!!!
//
// no 'break' in this loop, we're entering
// a critical section!
//
#if RMS_CRITICAL_SECTION
// <<<<< ENTER SINGLE THREADED SECTION
WsbAffirmHr(Lock());
#endif
WsbTrace(OLESTR("Refs = %d\n"), m_MountReference);
if (0 == m_MountReference) {
FILETIME now;
GetSystemTimeAsFileTime(&now);
ULARGE_INTEGER time0;
ULARGE_INTEGER time1;
time0.LowPart = m_UnloadNowTime.dwLowDateTime;
time0.HighPart = m_UnloadNowTime.dwHighDateTime;
time1.LowPart = now.dwLowDateTime;
time1.HighPart = now.dwHighDateTime;
// time0 is the target time for dismount.
// When delta goes negative, we've expired our
// wait time.
delta.QuadPart = time0.QuadPart-time1.QuadPart;
// convert delta to 100-ns to milliseconds
delta.QuadPart /= 10000;
WsbTrace(OLESTR("Time = <%ls>; Unload Time = <%ls>; delta = %I64d (ms)\n"),
WsbQuickString(WsbFiletimeAsString(FALSE, now)),
WsbQuickString(WsbFiletimeAsString(FALSE, m_UnloadNowTime)),
delta.QuadPart);
if (delta.QuadPart <= 0) {
// Dismount wait time has expired
// Double check that we still have something to dismount
if (S_OK == IsOccupied()) {
// Best effort home
// Fixed drives are always occupied and we shouldn't call Home for their cartridge
FlushBuffers();
if (RmsDeviceFixedDisk != m_deviceType) {
if (S_OK == m_pCartridge->Home()) {
SetIsOccupied(FALSE);
}
}
}
m_UnloadThreadHandle = NULL;
waiting = FALSE;
SetEvent(m_UnloadedEvent);
}
}
else {
hr = S_FALSE;
m_UnloadThreadHandle = NULL;
waiting = FALSE;
}
#if RMS_CRITICAL_SECTION
WsbAffirmHr(Unlock());
// >>>>> LEAVE SINGLE THREADED SECTION
#endif
if ( waiting ) {
switch(WaitForSingleObject(m_UnloadNowEvent, delta.LowPart)) {
case WAIT_FAILED:
WsbTrace(OLESTR("CRmsDrive::Unload - Wait for Single Object returned error: %ls\n"),
WsbHrAsString(HRESULT_FROM_WIN32(GetLastError())));
break;
case WAIT_TIMEOUT:
WsbTrace(OLESTR("CRmsDrive::Unload - Awakened by timeout.\n"));
break;
default:
WsbTrace(OLESTR("CRmsDrive::Unload - Awakened by external signal.\n"));
GetSystemTimeAsFileTime(&m_UnloadNowTime);
break;
}
}
} // waiting
} WsbCatch(hr);
WsbTraceOut(OLESTR("CRmsDrive::Unload"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CRmsDrive::FlushBuffers( void )
/*++
Implements:
IRmsDrive::FlushBuffers
--*/
{
HRESULT hr S_OK;
WsbTraceIn(OLESTR("CRmsDrive::FlushBuffers"), OLESTR("Device=<%ls>"), (WCHAR *) m_deviceName);
HANDLE hDrive = INVALID_HANDLE_VALUE;
try {
// First flush system buffers
switch (m_mediaSupported) {
case RmsMedia8mm:
case RmsMedia4mm:
case RmsMediaDLT:
case RmsMediaTape:
case RmsMediaWORM:
break;
case RmsMediaOptical:
case RmsMediaMO35:
case RmsMediaCDR:
case RmsMediaDVD:
case RmsMediaDisk:
// No need to flush for Optical media - RSM should flush the system buffers before dismounting
break;
case RmsMediaFixed:
{
// This is special code to flush the file system buffers.
// Create an exclusive handle
CWsbStringPtr drive;
WsbAffirmHr(drive.Alloc(10));
wcsncat( drive, m_deviceName, 2 );
drive.Prepend( OLESTR( "\\\\.\\" ) );
hDrive = CreateFile( drive,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
NULL
);
WsbAffirmHandle(hDrive);
// Flush buffers
WsbAffirmStatus(FlushFileBuffers(hDrive));
CloseHandle(hDrive);
hDrive = INVALID_HANDLE_VALUE;
}
break;
}
} WsbCatchAndDo(hr,
if (INVALID_HANDLE_VALUE != hDrive) {
CloseHandle(hDrive);
}
);
WsbTraceOut(OLESTR("CRmsDrive::FlushBuffers"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CRmsDrive::Lock( void )
/*++
Implements:
IRmsDrive::Lock
--*/
{
HRESULT hr S_OK;
WsbTraceIn(OLESTR("CRmsDrive::Lock"), OLESTR(""));
try {
try {
// InitializeCriticalSection raises an exception. Enter/Leave may too.
EnterCriticalSection(&m_CriticalSection);
} catch(DWORD status) {
WsbLogEvent(status, 0, NULL, NULL);
WsbThrow(E_UNEXPECTED);
}
} WsbCatch(hr)
WsbTraceOut(OLESTR("CRmsDrive::Lock"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}
HRESULT
CRmsDrive::Unlock( void )
/*++
Implements:
IRmsDrive::Unlock
--*/
{
HRESULT hr S_OK;
WsbTraceIn(OLESTR("CRmsDrive::Unlock"), OLESTR(""));
try {
try {
// InitializeCriticalSection raises an exception. Enter/Leave may too.
LeaveCriticalSection(&m_CriticalSection);
} catch(DWORD status) {
WsbLogEvent(status, 0, NULL, NULL);
WsbThrow(E_UNEXPECTED);
}
} WsbCatch(hr)
WsbTraceOut(OLESTR("CRmsDrive::Unlock"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return hr;
}