/*++ © 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 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 pDrive1; CComPtr pDrive2; CComPtr pFile1; CComPtr 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 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 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 pServer = g_pServer; CComPtr 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 pServer = g_pServer; CComPtr 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 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 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; }