1363 lines
31 KiB
C++
1363 lines
31 KiB
C++
/*++
|
||
|
||
© 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;
|
||
}
|
||
|