/*++ Module Name: hsmreclq.cpp Abstract: This class represents the HSM Demand Recall Queue manager It handles recalls initiated by users accessing HSM managed files. Based on the regular HSM queue manager (CHsmWorkQueue) Author: Ravisankar Pudipeddi [ravisp] 1 Oct. 1999 Revision History: --*/ #include "stdafx.h" #define WSB_TRACE_IS WSB_TRACE_BIT_HSMTSKMGR static USHORT iCount = 0; #include "fsa.h" #include "rms.h" #include "metadef.h" #include "jobint.h" #include "hsmconn.h" #include "wsb.h" #include "hsmeng.h" #include "mover.h" #include "hsmreclq.h" #include "engine.h" #include "task.h" #include "tskmgr.h" #include "segdb.h" #define STRINGIZE(_str) (OLESTR( #_str )) #define RETURN_STRINGIZED_CASE(_case) \ case _case: \ return ( STRINGIZE( _case ) ); // Local prototypes DWORD HsmRecallQueueThread(void *pVoid); static const OLECHAR * JobStateAsString (HSM_JOB_STATE state); static const OLECHAR * JobStateAsString ( IN HSM_JOB_STATE state ) /*++ Routine Description: Gives back a static string representing the connection state. Arguments: state - the state to return a string for. Return Value: NULL - invalid state passed in. Otherwise, a valid char *. --*/ { // // Do the Switch // switch (state) { RETURN_STRINGIZED_CASE( HSM_JOB_STATE_ACTIVE ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_CANCELLED ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_CANCELLING ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_DONE ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_FAILED ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_IDLE ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_PAUSED ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_PAUSING ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_RESUMING ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_SKIPPED ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_STARTING ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_SUSPENDED ); RETURN_STRINGIZED_CASE( HSM_JOB_STATE_SUSPENDING ); default: return( OLESTR("Invalid Value") ); } } HRESULT CHsmRecallQueue::FinalConstruct( void ) /*++ Routine Description: This method does some initialization of the object that is necessary after construction. Arguments: None. Return Value: S_OK Anything returned by CWsbCollectable::FinalConstruct(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::FinalConstruct"),OLESTR("")); try { WsbAssertHr(CComObjectRoot::FinalConstruct()); // // Initialize the member data // m_pServer = 0; m_pHsmServerCreate = 0; m_pTskMgr = 0; m_pRmsServer = 0; m_pRmsCartridge = 0; m_pDataMover = 0; m_pWorkToDo = 0; UnsetMediaInfo(); m_HsmId = GUID_NULL; m_RmsMediaSetId = GUID_NULL; m_RmsMediaSetName = OLESTR(""); m_QueueType = HSM_WORK_TYPE_FSA_DEMAND_RECALL; m_JobPriority = HSM_JOB_PRIORITY_NORMAL; m_WorkerThread = 0; m_CurrentPath = OLESTR(""); // Job abort on errors parameters m_JobAbortMaxConsecutiveErrors = 5; m_JobAbortMaxTotalErrors = 25; m_JobConsecutiveErrors = 0; m_JobTotalErrors = 0; m_JobAbortSysDiskSpace = 2 * 1024 * 1024; m_CurrentSeekOffset = 0; WSB_OBJECT_ADD(CLSID_CHsmRecallQueue, this); }WsbCatch(hr); iCount++; WsbTraceOut(OLESTR("CHsmRecallQueue::FinalConstruct"),OLESTR("hr = <%ls>, Count is <%d>"), WsbHrAsString(hr), iCount); return(hr); } HRESULT CHsmRecallQueue::FinalRelease( void ) /*++ Routine Description: This method does some initialization of the object that is necessary before destruction. Arguments: None. Return Value: S_OK Anything returned by CWsbCollection::FinalDestruct(). --*/ { HRESULT hr = S_OK; HSM_SYSTEM_STATE SysState; WsbTraceIn(OLESTR("CHsmRecallQueue::FinalRelease"),OLESTR("")); SysState.State = HSM_STATE_SHUTDOWN; ChangeSysState(&SysState); WSB_OBJECT_SUB(CLSID_CHsmRecallQueue, this); CComObjectRoot::FinalRelease(); // Free String members // Note: Member objects held in smart-pointers are freed when the // smart-pointer destructor is being called (as part of this object destruction) m_MediaName.Free(); m_MediaBarCode.Free(); m_RmsMediaSetName.Free(); m_CurrentPath.Free(); iCount--; WsbTraceOut(OLESTR("CHsmRecallQueue::FinalRelease"),OLESTR("hr = <%ls>, Count is <%d>"), WsbHrAsString(hr), iCount); return(hr); } HRESULT CHsmRecallQueue::Init( IUnknown *pServer, IHsmFsaTskMgr *pTskMgr ) /*++ Routine Description: This method does some initialization of the object that is necessary after construction. Arguments: Return Value: --*/ { HRESULT hr = S_OK; LONG rmsCartridgeType; CComPtr pMedia; WsbTraceIn(OLESTR("CHsmRecallQueue::Init"),OLESTR("")); try { // // Establish contact with the server and get the // databases // WsbAffirmHr(pServer->QueryInterface(IID_IHsmServer, (void **)&m_pServer)); //We want a weak link to the server so decrement the reference count m_pServer->Release(); m_pTskMgr = pTskMgr; m_pTskMgr->AddRef(); m_QueueType = HSM_WORK_TYPE_FSA_DEMAND_RECALL; WsbAffirmHr(m_pServer->QueryInterface(IID_IWsbCreateLocalObject, (void **)&m_pHsmServerCreate)); // We want a weak link to the server so decrement the reference count m_pHsmServerCreate->Release(); WsbAffirmHr(m_pServer->GetID(&m_HsmId)); // // Make sure our connection to RMS is current // WsbAffirmHr(CheckRms()); // // Get the media type. We assume mediaId is set before this // is called. Imperative! // WsbAffirmHr(m_pRmsServer->FindCartridgeById(m_MediaId, &pMedia)); WsbAffirmHr(pMedia->GetType( &rmsCartridgeType )); WsbAffirmHr(ConvertRmsCartridgeType(rmsCartridgeType, &m_MediaType)); // // Make a collection for the work items // WsbAffirmHr(m_pHsmServerCreate->CreateInstance(CLSID_CWsbOrderedCollection, IID_IWsbIndexedCollection, (void **)&m_pWorkToDo )); // Check the registry to see if there are changes to default settings CheckRegistry(); }WsbCatch( hr ); WsbTraceOut(OLESTR("CHsmRecallQueue::Init"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return( hr ); } HRESULT CHsmRecallQueue::ContactOk( void ) /*++ Routine Description: This allows the caller to see if the RPC connection to the task manager is OK Arguments: None. Return Value: S_OK --*/ { return( S_OK ); } HRESULT CHsmRecallQueue::ProcessSessionEvent( IHsmSession *pSession, HSM_JOB_PHASE phase, HSM_JOB_EVENT event ) /*++ Routine Description: Arguments: Return Value: --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::ProcessSessionEvent"),OLESTR("")); try { WsbAssert(0 != pSession, E_POINTER); // If the phase applies to us (MOVER or ALL), then do any work required by the // event. if ((HSM_JOB_PHASE_ALL == phase) || (HSM_JOB_PHASE_MOVE_ACTION == phase)) { switch (event) { case HSM_JOB_EVENT_SUSPEND: case HSM_JOB_EVENT_CANCEL: case HSM_JOB_EVENT_FAIL: WsbAffirmHr(Cancel(phase, pSession)); break; case HSM_JOB_EVENT_RAISE_PRIORITY: WsbAffirmHr(RaisePriority(phase, pSession)); break; case HSM_JOB_EVENT_LOWER_PRIORITY: WsbAffirmHr(LowerPriority(phase, pSession)); break; default: case HSM_JOB_EVENT_START: WsbAssert(FALSE, E_UNEXPECTED); break; } } }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::ProcessSessionEvent"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return( S_OK ); } HRESULT CHsmRecallQueue::ProcessSessionState( IHsmSession* /*pSession*/, IHsmPhase* pPhase, OLECHAR* /*currentPath*/ ) /*++ Routine Description: Arguments: Return Value: --*/ { HRESULT hr = S_OK; HSM_JOB_PHASE phase; HSM_JOB_STATE state; WsbTraceIn(OLESTR("CHsmRecallQueue::ProcessSessionState"),OLESTR("")); try { WsbAffirmHr(pPhase->GetState(&state)); WsbAffirmHr(pPhase->GetPhase(&phase)); WsbTrace( OLESTR("CHsmRecallQueue::ProcessSessionState - State = <%d>, phase = <%d>\n"), state, phase ); if (HSM_JOB_PHASE_SCAN == phase) { // If the session has finished, then we have some cleanup to do so that it can go // away. if ((state == HSM_JOB_STATE_DONE) || (state == HSM_JOB_STATE_FAILED) || (state == HSM_JOB_STATE_SUSPENDED)) { WsbTrace( OLESTR("Job is done, failed, or suspended\n") ); // // Do nothing: when one recall item is done, we don't need to wait // for the FSA in order to perform cleanup code // /*** WsbAffirmHr(MarkWorkItemAsDone(pSession, phase)); ***/ } } }WsbCatch( hr ); WsbTraceOut(OLESTR("CHsmRecallQueue::ProcessSessionState"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return( S_OK ); } HRESULT CHsmRecallQueue::Add( IFsaPostIt *pFsaWorkItem, GUID *pBagId, LONGLONG dataSetStart ) /*++ Implements: IHsmFsaTskMgr::Add --*/ { HRESULT hr = S_OK; CComPtr pSession; CComPtr pEnum; CComPtr pWorkItem, pWorkItem2; LONGLONG seekOffset, currentSeekOffset, prevSeekOffset; LARGE_INTEGER remoteFileStart, remoteDataStart; LONGLONG readOffset; FSA_PLACEHOLDER placeholder; HSM_WORK_ITEM_TYPE workType; BOOL workItemAllocated=FALSE, insert; CComPtr pRecall; DWORD index = 0; BOOL qLocked = FALSE; WsbTraceIn(OLESTR("CHsmRecallQueue::Add"),OLESTR("")); try { // // Make sure there is a work allocater for this session // WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); // // Create a work item, load it up and add it to this // Queue's collection // CComPtr pWorkItem; WsbAffirmHr(m_pHsmServerCreate->CreateInstance(CLSID_CHsmRecallItem, IID_IHsmRecallItem, (void **)&pWorkItem)); workItemAllocated = TRUE; WsbAffirmHr(pWorkItem->SetWorkType(HSM_WORK_ITEM_FSA_WORK)); WsbAffirmHr(pWorkItem->SetFsaPostIt(pFsaWorkItem)); WsbAffirmHr(CheckSession(pSession, pWorkItem)); WsbAffirmHr(pWorkItem->SetBagId(pBagId)); WsbAffirmHr(pWorkItem->SetDataSetStart(dataSetStart)); if (m_MediaType == HSM_JOB_MEDIA_TYPE_TAPE) { // // For sequential media we order the requests to achieve // optimal perf. // WsbAffirmHr(pFsaWorkItem->GetPlaceholder(&placeholder)); remoteFileStart.QuadPart = placeholder.fileStart; remoteDataStart.QuadPart = placeholder.dataStart; WsbAffirmHr(pFsaWorkItem->GetFilterRecall(&pRecall)); WsbAffirmHr(pRecall->GetOffset( &readOffset )); // // Calculate the offset in the media that needs to be seeked to // for the recall. This will be only used for ordering the queue // performance reasons. // seekOffset = dataSetStart + remoteFileStart.QuadPart + remoteDataStart.QuadPart + readOffset; WsbAffirmHr(pWorkItem->SetSeekOffset(seekOffset)); index = 0; // // Find a position in the queue to insert it // First, we lock the queue while we search for the position // & insert the item into the queue. We make the assumption // the lock protecting the queue is recursively acquirable. // If this is not true, the code that adds to the queue will // deadlock because it tries to lock the queue too! // m_pWorkToDo->Lock(); qLocked = TRUE; WsbAffirmHr(m_pWorkToDo->Enum(&pEnum)); // // If the seek offset of the item we wish to insert is // > the current seek offset of the item that is in progress, // we just insert it in the first monotonic ascending sequence. // If not, we insert in the *second* monotonic ascending sequence, // to prevent the head from seeking back prematurely // hr = pEnum->First(IID_IHsmRecallItem, (void **)&pWorkItem2); if (seekOffset > m_CurrentSeekOffset) { // // Insert in the first ascending sequence // insert = TRUE; } else { // // Skip the first ascending sequence // insert = FALSE; } prevSeekOffset = 0; while (hr != WSB_E_NOTFOUND) { WsbAffirmHr(pWorkItem2->GetWorkType(&workType)); if (workType != HSM_WORK_ITEM_FSA_WORK) { // // Not interested in this. Release it before getting the next // pWorkItem2 = 0; hr = pEnum->Next(IID_IHsmRecallItem, (void **)&pWorkItem2); index++; continue; } WsbAffirmHr(pWorkItem2->GetSeekOffset(¤tSeekOffset)); if (insert && (currentSeekOffset > seekOffset)) { // // place to insert the item.. // break; } if (!insert && (currentSeekOffset < prevSeekOffset)) { // // Start of second monotone sequence. We wish to insert // the item in this sequence // insert = TRUE; // // Check if pWorkItem is eligible to be inserted at this // index position // if (currentSeekOffset > seekOffset) { // // place to insert the item.. // break; } } else { prevSeekOffset = currentSeekOffset; } // // Move on to the next. Release the current item first // pWorkItem2 = 0; hr = pEnum->Next(IID_IHsmRecallItem, (void **)&pWorkItem2); index++; } if (hr == WSB_E_NOTFOUND) { WsbAffirmHr(m_pWorkToDo->Append(pWorkItem)); } else { WsbAffirmHr(m_pWorkToDo->AddAt(pWorkItem, index)); } // // Safe to unlock the queue // m_pWorkToDo->Unlock(); qLocked = FALSE; } else { // // For non-sequential media, we just add it to the queue ... // No ordering is done, we let the file system do the optimizations // WsbAffirmHr(m_pWorkToDo->Append(pWorkItem)); } hr = S_OK; }WsbCatchAndDo(hr, // // Add code to release queue lock if acquired // if (qLocked) { m_pWorkToDo->Unlock(); } ); WsbTraceOut(OLESTR("CHsmRecallQueue::Add"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::Start( void ) /*++ Implements: IHsmRecallQueue::Start --*/ { HRESULT hr = S_OK; DWORD tid; WsbTraceIn(OLESTR("CHsmRecallQueue::Start"),OLESTR("")); try { // // If the worker thread is already started, just return // WsbAffirm(m_WorkerThread == 0, S_OK); // Launch a thread to do work that is queued WsbAffirm((m_WorkerThread = CreateThread(0, 0, HsmRecallQueueThread, (void*) this, 0, &tid)) != 0, HRESULT_FROM_WIN32(GetLastError())); if (m_WorkerThread == NULL) { WsbAssertHr(E_FAIL); // TBD What error to return here?? } }WsbCatch (hr); WsbTraceOut(OLESTR("CHsmRecallQueue::Start"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::Stop( void ) /*++ Implements: IHsmRecallQueue::Stop --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::Stop"),OLESTR("")); // Stop the thread if (m_WorkerThread) { TerminateThread(m_WorkerThread, 0); } WsbTraceOut(OLESTR("CHsmRecallQueue::Stop"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::RecallIt( IHsmRecallItem * pWorkItem ) { HRESULT hr = S_OK; CComPtr pScanItem; CComPtr pFsaWorkItem; LONGLONG readOffset; FSA_REQUEST_ACTION requestAction; ULARGE_INTEGER remoteDataSetStart; GUID bagId; CComPtr pMountingCollection; CComPtr pMountingMedia; CComPtr pMediaToFind; BOOL bMediaMounting = FALSE; BOOL bMediaMountingAdded = FALSE; WsbTraceIn(OLESTR("CHsmRecallQueue::RecallIt"),OLESTR("")); try { WsbAffirmHr(pWorkItem->GetFsaPostIt(&pFsaWorkItem)); WsbAffirmHr(pFsaWorkItem->GetRequestAction(&requestAction)); GetScanItem(pFsaWorkItem, &pScanItem); WsbAffirmHr(pWorkItem->GetBagId(&bagId)); WsbAffirmHr(pWorkItem->GetDataSetStart((LONGLONG *) &remoteDataSetStart.QuadPart)); // // Check if we are mounting a new media: recall-queue is created on a per-media basis, therefore, // media cannot change. The only test is whether the media for this queue is already mounted // if (m_MountedMedia == GUID_NULL) { // Check if the media is already in the process of mounting WsbAffirmHr(m_pServer->LockMountingMedias()); try { // Check if the media to mount is already mounting WsbAffirmHr(m_pServer->GetMountingMedias(&pMountingCollection)); WsbAffirmHr(CoCreateInstance(CLSID_CMountingMedia, 0, CLSCTX_SERVER, IID_IMountingMedia, (void**)&pMediaToFind)); WsbAffirmHr(pMediaToFind->SetMediaId(m_MediaId)); hr = pMountingCollection->Find(pMediaToFind, IID_IMountingMedia, (void **)&pMountingMedia); if (hr == S_OK) { // Media is already mounting... bMediaMounting = TRUE; } else if (hr == WSB_E_NOTFOUND) { // New media to mount - add to the mounting list hr = S_OK; WsbAffirmHr(pMediaToFind->Init(m_MediaId, TRUE)); WsbAffirmHr(pMountingCollection->Add(pMediaToFind)); bMediaMountingAdded = TRUE; } else { WsbAffirmHr(hr); } } WsbCatchAndDo(hr, // Unlock mounting media m_pServer->UnlockMountingMedias(); WsbTraceAlways(OLESTR("CHsmRecallQueue::RecallIt: error while trying to find/add mounting media. hr=<%ls>\n"), WsbHrAsString(hr)); // Bale out WsbThrow(hr); ); // Release the lock WsbAffirmHr(m_pServer->UnlockMountingMedias()); } // // If the media is already mounting - wait for the mount event // if (bMediaMounting) { WsbAffirmHr(pMountingMedia->WaitForMount(INFINITE)); pMountingMedia = 0; } // // Get the media mounted (hr is checked only after removing from the mounting-media list) // hr = MountMedia(pWorkItem, m_MediaId); // // If added to the mounting list - remove // if (bMediaMountingAdded) { HRESULT hrRemove = S_OK; // No matter how the Mount finished - free waiting clients and remove from the list hrRemove = m_pServer->LockMountingMedias(); WsbAffirmHr(hrRemove); try { WsbAffirmHr(pMediaToFind->MountDone()); WsbAffirmHr(pMountingCollection->RemoveAndRelease(pMediaToFind)); pMediaToFind = 0; } WsbCatch(hrRemove); m_pServer->UnlockMountingMedias(); // We don't expect any errors while removing the mounting media - // The thread that added to the collection is always the one that removes if (! SUCCEEDED(hrRemove)) { WsbTraceAlways(OLESTR("CHsmRecallQueue::RecallIt: error while trying to remove a mounting media. hr=<%ls>\n"), WsbHrAsString(hrRemove)); WsbThrow(hrRemove); } } // // Check the Mount result // WsbAffirmHr(hr); // // Copy the data // // Build the source path CWsbStringPtr tmpString; WsbAffirmHr(GetSource(pFsaWorkItem, &tmpString)); CWsbBstrPtr localName = tmpString; // Ask the Data mover to store the data LONGLONG requestSize; LONGLONG requestStart; ULARGE_INTEGER localDataStart; ULARGE_INTEGER localDataSize; ULARGE_INTEGER remoteFileStart; ULARGE_INTEGER remoteFileSize; ULARGE_INTEGER remoteDataStart; ULARGE_INTEGER remoteDataSize; ULARGE_INTEGER remoteVerificationData; ULONG remoteVerificationType; FSA_PLACEHOLDER placeholder; WsbAffirmHr(pFsaWorkItem->GetPlaceholder(&placeholder)); WsbAffirmHr(pFsaWorkItem->GetRequestSize(&requestSize)); WsbAffirmHr(pFsaWorkItem->GetRequestOffset(&requestStart)); // // Build strings // CWsbBstrPtr sessionName = HSM_BAG_NAME; CWsbBstrPtr sessionDescription = HSM_ENGINE_ID; sessionName.Append(WsbGuidAsString(bagId)); sessionDescription.Append(WsbGuidAsString(m_HsmId)); localDataStart.QuadPart = requestStart; localDataSize.QuadPart = requestSize; remoteFileStart.QuadPart = placeholder.fileStart; remoteFileSize.QuadPart = placeholder.fileSize; remoteDataStart.QuadPart = placeholder.dataStart; remoteDataSize.QuadPart = placeholder.dataSize; remoteVerificationData.QuadPart = placeholder.verificationData; remoteVerificationType = placeholder.verificationType; ReportMediaProgress(HSM_JOB_MEDIA_STATE_TRANSFERRING, hr); CComPtr pLocalStream; CComPtr pRemoteStream; ULARGE_INTEGER offset, read, written; DWORD verifyType; // // We are doing a demand recall, so get the // recall object's data mover // CComPtr pRecall; WsbAffirmHr(pFsaWorkItem->GetFilterRecall(&pRecall)); WsbAffirmHr(pRecall->CreateLocalStream( &pLocalStream)); WsbAffirmHr(pRecall->GetSize( (LONGLONG *) &remoteDataSize.QuadPart )); WsbAffirmHr(pRecall->GetOffset( &readOffset )); if (readOffset == 0) { verifyType = MVR_VERIFICATION_TYPE_HEADER_CRC; } else { verifyType = MVR_VERIFICATION_TYPE_NONE; } // // Set the current seek offset used for ordering items in the queue // m_CurrentSeekOffset = remoteDataSetStart.QuadPart + remoteFileStart.QuadPart+remoteDataStart.QuadPart+requestStart; // // Create remote data mover stream // TEMPORARY: Consider removing the NO_CACHING flag for a NO_RECALL recall // WsbAssert(0 != remoteDataSetStart.QuadPart, HSM_E_BAD_SEGMENT_INFORMATION); WsbAffirmHr( m_pDataMover->CreateRemoteStream( CWsbBstrPtr(L""), MVR_MODE_READ | MVR_FLAG_HSM_SEMANTICS | MVR_FLAG_NO_CACHING, sessionName, sessionDescription, remoteDataSetStart, remoteFileStart, remoteFileSize, remoteDataStart, remoteDataSize, verifyType, remoteVerificationData, &pRemoteStream ) ); // // The offset given here is the offset into the stream itself (readOffset). // The actual position on remote media will be the bag start plus the file start // plus the file-data start (all given in CreateRemoteStream) plus this offset. // WsbTrace(OLESTR("Setting offset to %I64d reading %I64u\n"), readOffset, remoteDataSize.QuadPart); offset.QuadPart = readOffset; WsbAffirmHr( m_pDataMover->SetInitialOffset( offset ) ); // // Once the remote stream has been created we must make sure we detach it // try { WsbAffirmHr( pRemoteStream->CopyTo( pLocalStream, remoteDataSize, &read, &written ) ); WsbAffirmHr( pLocalStream->Commit( STGC_DEFAULT ) ); // // The CopyTo succeeded... make sure we got all the bytes // we asked for. // // If we attempt to read from a incomplete Master that // does not contain the migrated data we'll fail here with // MVR_S_NO_DATA_DETECTED. // WsbAffirm( written.QuadPart == remoteDataSize.QuadPart, HSM_E_VALIDATE_DATA_NOT_ON_MEDIA ); WsbAffirmHr( m_pDataMover->CloseStream() ); }WsbCatchAndDo(hr, WsbAffirmHr( m_pDataMover->CloseStream() ); ); ReportMediaProgress(HSM_JOB_MEDIA_STATE_TRANSFERRED, hr); WsbTrace(OLESTR("RecallData returned hr = <%ls>\n"),WsbHrAsString(hr)); }WsbCatch( hr ); // Tell the session whether or not the work was done. // We don't really care about the return code, there is nothing // we can do if it fails. WsbTrace(OLESTR("Tried HSM work, calling Session to Process Item\n")); if (pScanItem) { CComPtr pSession; HSM_JOB_PHASE jobPhase; WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); WsbAffirmHr(pWorkItem->GetJobPhase(&jobPhase)); pSession->ProcessItem(jobPhase, HSM_JOB_ACTION_RECALL , pScanItem, hr); } WsbTraceOut(OLESTR("CHsmRecallQueue::RecallIt"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return( hr ); } HRESULT CHsmRecallQueue::RaisePriority( IN HSM_JOB_PHASE jobPhase, IN IHsmSession *pSession ) /*++ --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::RaisePriority"),OLESTR("")); try { WsbAssert(0 != m_WorkerThread, E_UNEXPECTED); WsbAssert(pSession != 0, E_UNEXPECTED); switch (m_JobPriority) { case HSM_JOB_PRIORITY_IDLE: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_LOWEST)); m_JobPriority = HSM_JOB_PRIORITY_LOWEST; break; case HSM_JOB_PRIORITY_LOWEST: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_BELOW_NORMAL)); m_JobPriority = HSM_JOB_PRIORITY_LOW; break; case HSM_JOB_PRIORITY_LOW: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_NORMAL)); m_JobPriority = HSM_JOB_PRIORITY_NORMAL; break; case HSM_JOB_PRIORITY_NORMAL: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_ABOVE_NORMAL)); m_JobPriority = HSM_JOB_PRIORITY_HIGH; break; case HSM_JOB_PRIORITY_HIGH: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_HIGHEST)); m_JobPriority = HSM_JOB_PRIORITY_HIGHEST; break; case HSM_JOB_PRIORITY_HIGHEST: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_TIME_CRITICAL)); m_JobPriority = HSM_JOB_PRIORITY_CRITICAL; break; default: case HSM_JOB_PRIORITY_CRITICAL: WsbAffirm(FALSE, E_UNEXPECTED); break; } WsbAffirmHr(pSession->ProcessPriority(jobPhase, m_JobPriority)); }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::RaisePriority"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::LowerPriority( IN HSM_JOB_PHASE jobPhase, IN IHsmSession *pSession ) /*++ --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::LowerPriority"),OLESTR("")); try { WsbAssert(0 != m_WorkerThread, E_UNEXPECTED); WsbAssert(pSession != 0, E_UNEXPECTED); switch (m_JobPriority) { case HSM_JOB_PRIORITY_IDLE: WsbAffirm(FALSE, E_UNEXPECTED); break; case HSM_JOB_PRIORITY_LOWEST: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_IDLE)); m_JobPriority = HSM_JOB_PRIORITY_IDLE; break; case HSM_JOB_PRIORITY_LOW: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_LOWEST)); m_JobPriority = HSM_JOB_PRIORITY_LOWEST; break; case HSM_JOB_PRIORITY_NORMAL: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_BELOW_NORMAL)); m_JobPriority = HSM_JOB_PRIORITY_LOW; break; case HSM_JOB_PRIORITY_HIGH: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_NORMAL)); m_JobPriority = HSM_JOB_PRIORITY_NORMAL; break; case HSM_JOB_PRIORITY_HIGHEST: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_ABOVE_NORMAL)); m_JobPriority = HSM_JOB_PRIORITY_HIGH; break; default: case HSM_JOB_PRIORITY_CRITICAL: WsbAffirmStatus(SetThreadPriority(m_WorkerThread, THREAD_PRIORITY_HIGHEST)); m_JobPriority = HSM_JOB_PRIORITY_HIGHEST; break; } WsbAffirmHr(pSession->ProcessPriority(jobPhase, m_JobPriority)); }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::LowerPriority"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::CheckRms( void ) /*++ --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::CheckRms"),OLESTR("")); try { // // Make sure we can still talk to the RMS // if (m_pRmsServer != 0) { CWsbBstrPtr name; hr = m_pRmsServer->GetServerName( &name ); if (hr != S_OK) { m_pRmsServer = 0; hr = S_OK; } } // // Get RMS that runs on this machine // if (m_pRmsServer == 0) { WsbAffirmHr(m_pServer->GetHsmMediaMgr(&m_pRmsServer)); // wait for RMS to come ready // (this may not be needed anymore - if Rms initialization is // synced with Engine initialization) CComObject *pSink = new CComObject; CComPtr pSinkUnk = pSink; // holds refcount for use here WsbAffirmHr( pSink->Construct( m_pRmsServer ) ); WsbAffirmHr( pSink->WaitForReady( ) ); WsbAffirmHr( pSink->DoUnadvise( ) ); } }WsbCatch( hr ); WsbTraceOut(OLESTR("CHsmRecallQueue::CheckRms"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::CheckSession( IHsmSession *pSession, IHsmRecallItem *pWorkItem ) /*++ --*/ { HRESULT hr = S_OK; BOOL bLog = TRUE; WsbTraceIn(OLESTR("CHsmRecallQueue::CheckSession"),OLESTR("")); try { // // Check to see if we have dealt with this or any other session before. WsbTrace(OLESTR("New session.\n")); // // We have no on going session so we need to establish communication // with this session. // CComPtr pSinkState; CComPtr pSinkEvent; CComPtr pCPC; CComPtr pCP; CComPtr pFsaResource; HSM_JOB_PHASE jobPhase; DWORD stateCookie, eventCookie; ULONG refCount; // Tell the session we are starting up. pWorkItem->SetJobState(HSM_JOB_STATE_STARTING); pWorkItem->GetJobPhase(&jobPhase); WsbTrace(OLESTR("Before Process State.\n")); refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue before 1st process state: %ls \n"), WsbLongAsString((LONG) refCount)); WsbAffirmHr(pSession->ProcessState(jobPhase, HSM_JOB_STATE_STARTING, m_CurrentPath, bLog)); refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue after 1st process state: %ls \n"), WsbLongAsString((LONG) refCount)); WsbTrace(OLESTR("After Process State.\n")); // Get the interface to the callback that the sessions should use. WsbTrace(OLESTR("Before QI's for sinks.\n")); WsbAffirmHr(((IUnknown*) (IHsmFsaTskMgr*) this)->QueryInterface(IID_IHsmSessionSinkEveryState, (void**) &pSinkState)); WsbAffirmHr(((IUnknown*) (IHsmFsaTskMgr*) this)->QueryInterface(IID_IHsmSessionSinkEveryEvent, (void**) &pSinkEvent)); WsbTrace(OLESTR("After QI's for sinks.\n")); // Ask the session to advise of every state changes. WsbTrace(OLESTR("Before QI for connection point containers.\n")); WsbAffirmHr(pSession->QueryInterface(IID_IConnectionPointContainer, (void**) &pCPC)); WsbAffirmHr(pCPC->FindConnectionPoint(IID_IHsmSessionSinkEveryState, &pCP)); WsbAffirmHr(pCP->Advise(pSinkState, &stateCookie)); pWorkItem->SetStateCookie(stateCookie); pCP = 0; WsbAffirmHr(pCPC->FindConnectionPoint(IID_IHsmSessionSinkEveryEvent, &pCP)); WsbAffirmHr(pCP->Advise(pSinkEvent, &eventCookie)); pWorkItem->SetEventCookie(eventCookie); pCP = 0; WsbTrace(OLESTR("After Advises.\n")); // // Get the resource for this work from the session // WsbAffirmHr(pSession->GetResource(&pFsaResource)); pWorkItem->SetJobState(HSM_JOB_STATE_ACTIVE); WsbTrace(OLESTR("Before Process State.\n")); refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue before 2nd process state: %ls \n"), WsbLongAsString((LONG) refCount)); WsbAffirmHr(pSession->ProcessState(jobPhase, HSM_JOB_STATE_ACTIVE, m_CurrentPath, bLog)); refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue after 2nd process state: %ls \n"), WsbLongAsString((LONG) refCount)); WsbTrace(OLESTR("After Process State.\n")); }WsbCatch( hr ); WsbTraceOut(OLESTR("CHsmRecallQueue::CheckSession"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::DoWork( void ) /*++ --*/ { HRESULT hr = S_OK; CWsbStringPtr path; CComPtr pWorkItem; HSM_WORK_ITEM_TYPE workType; BOOLEAN done = FALSE; HRESULT skipWork = S_FALSE; WsbTraceIn(OLESTR("CHsmRecallQueue::DoWork"),OLESTR("")); // Make sure this object isn't released (and our thread killed // before finishing up in this routine ((IUnknown*)(IHsmRecallQueue*)this)->AddRef(); try { while (!done) { // // Get the next work to do from the queue // hr = m_pWorkToDo->First(IID_IHsmRecallItem, (void **)&pWorkItem); if (WSB_E_NOTFOUND == hr) { // // We might be done with this queue. // Attempt to destroy it: if we cannot it means there are more items // that were being added so we continue looping // hr = m_pTskMgr->WorkQueueDone(NULL, HSM_WORK_TYPE_FSA_DEMAND_RECALL, &m_MediaId); if (hr == S_OK) { // // Queue is really done - break out of the while loop // done = TRUE; break; } else if (hr == S_FALSE) { // // More items in the queue // continue; } else { // // Some sort of error happened, bale out // WsbTraceAlways(OLESTR("CHsmRecallQueue::DoWork: WorkQueueDone failed with <%ls> - terminating queue thread\n"), WsbHrAsString(hr)); WsbAffirmHr(hr); } } else { WsbAffirmHr(hr); // // Remove it from the queue // Remove(pWorkItem); } WsbAffirmHr(pWorkItem->GetWorkType(&workType)); switch (workType) { case HSM_WORK_ITEM_FSA_DONE: { // // TBD:Code path should not be reached // WsbTraceAlways(OLESTR("Unexpected: CHsmRecallQueue::DoWork - FSA WORK DONE item\n")); break; } case HSM_WORK_ITEM_FSA_WORK: { if (S_FALSE == skipWork) { // // Get the FSA Work Item and do the work // hr = DoFsaWork(pWorkItem); } else { // // Skip the work // try { CComPtr pFsaWorkItem; CComPtr pScanItem; CComPtr pFsaResource; CComPtr pSession; HSM_JOB_PHASE jobPhase; WsbAffirmHr(pWorkItem->GetFsaPostIt(&pFsaWorkItem)); WsbAffirmHr(pWorkItem->GetJobPhase(&jobPhase)); WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); WsbAffirmHr(pSession->GetResource(&pFsaResource)); WsbAffirmHr(GetScanItem(pFsaWorkItem, &pScanItem)); hr = pFsaWorkItem->SetResult(skipWork); if (S_OK == hr) { WsbTrace(OLESTR("HSM recall (filter, read or recall) complete, calling FSA\n")); hr = pFsaResource->ProcessResult(pFsaWorkItem); WsbTrace(OLESTR("FSA ProcessResult returned <%ls>\n"), WsbHrAsString(hr)); } (void)pSession->ProcessHr(jobPhase, 0, 0, hr); WsbAffirmHr(pSession->ProcessItem(jobPhase, HSM_JOB_ACTION_RECALL, pScanItem, skipWork)); }WsbCatch( hr ); } EndRecallSession(pWorkItem, FALSE); break; } case HSM_WORK_ITEM_MOVER_CANCELLED: { CComPtr pWorkItemToCancel; WsbTrace(OLESTR("CHsmRecallQueue::DoWork - Mover Cancelled\n")); try { // // Get hold of the work item that needs to be cancelled. // This is indicated by the session pointer in the cancel work item // hr = FindRecallItemToCancel(pWorkItem, &pWorkItemToCancel); if (hr == S_OK) { EndRecallSession(pWorkItemToCancel, TRUE); // // Remove the *cancelled* work item // Remove(pWorkItemToCancel); } // // Remove the cancel work item // hr = S_OK; }WsbCatch( hr ); // // We are done completely with one more work item // break; } default: { hr = E_UNEXPECTED; break; } } pWorkItem = 0; } }WsbCatch( hr ); // // Dismount the media.. // DismountMedia(FALSE); // Pretend everything is OK hr = S_OK; // Release the thread (the thread should terminate on exit // from the routine that called this routine) CloseHandle(m_WorkerThread); m_WorkerThread = 0; // Allow this object to be released ((IUnknown*)(IHsmRecallQueue*)this)->Release(); WsbTraceOut(OLESTR("CHsmRecallQueue::DoWork"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::DoFsaWork( IHsmRecallItem *pWorkItem ) /*++ --*/ { HRESULT hr = S_OK; HRESULT hr2 = S_OK; HRESULT workHr = S_OK; HSM_JOB_PHASE jobPhase; CWsbStringPtr path; CComPtr pFsaWorkItem; CComPtr pSession; CComPtr pFsaResource; WsbTraceIn(OLESTR("CHsmRecallQueue::DoFsaWork"),OLESTR("")); try { // // Do the work. // WsbAffirmHr(pWorkItem->GetFsaPostIt(&pFsaWorkItem)); WsbAffirmHr(pWorkItem->GetJobPhase(&jobPhase)); WsbAffirmHr(pFsaWorkItem->GetPath(&path, 0)); WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); WsbAffirmHr(pSession->GetResource(&pFsaResource)); WsbTrace(OLESTR("Handling file <%s>.\n"), WsbAbbreviatePath(path, 120)); workHr = RecallIt(pWorkItem); // // Tell the recaller right away about the success or failure // of the recall, we do this here so the recall filter can // release the open as soon as possible // hr = pFsaWorkItem->SetResult(workHr); if (S_OK == hr) { WsbTrace(OLESTR("HSM recall (filter, read or recall) complete, calling FSA\n")); hr = pFsaResource->ProcessResult(pFsaWorkItem); WsbTrace(OLESTR("FSA ProcessResult returned <%ls>\n"), WsbHrAsString(hr)); } // Note: In case that the recall item is/was canceling at any time, we don't want // to report on errors. If the cancel occurred while the recall item was queued, // we won't get here at all, but if it was cancelled while being executed, we // might end up here with some bad workHr returned by the Mover code if ((S_OK != workHr) && (S_OK != pSession->IsCanceling())) { // Tell the session how things went if they didn't go well. (void) pSession->ProcessHr(jobPhase, 0, 0, workHr); } }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::DoFsaWork"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::MountMedia( IHsmRecallItem *pWorkItem, GUID mediaToMount, BOOL bShortWait ) /*++ --*/ { HRESULT hr = S_OK; GUID l_MediaToMount = mediaToMount; CComPtr pDrive; CWsbBstrPtr pMediaName; DWORD dwOptions = RMS_NONE; DWORD threadId; CComPtr pFsaWorkItem; WsbTraceIn(OLESTR("CHsmRecallQueue::MountMedia"),OLESTR("Display Name = <%ls>"), (WCHAR *)m_MediaName); try { WsbAffirmHr(pWorkItem->GetFsaPostIt(&pFsaWorkItem)); WsbAffirmHr(pFsaWorkItem->GetThreadId(&threadId)); // If we're switching tapes, dismount the current one if ((m_MountedMedia != l_MediaToMount) && (m_MountedMedia != GUID_NULL)) { WsbAffirmHr(DismountMedia()); } // Ask RMS for short timeout, both for Mount and Allocate if (bShortWait) { dwOptions |= RMS_SHORT_TIMEOUT; } dwOptions |= RMS_USE_MOUNT_NO_DEADLOCK; if (m_MountedMedia != l_MediaToMount) { ReportMediaProgress(HSM_JOB_MEDIA_STATE_MOUNTING, hr); hr = m_pRmsServer->MountCartridge( l_MediaToMount, &pDrive, &m_pRmsCartridge, &m_pDataMover, dwOptions, threadId); hr = TranslateRmsMountHr(hr); // // If failure is because cartridge is disabled, need to get media label to put in error. // if (hr == RMS_E_CARTRIDGE_DISABLED) { // Since this is just to get label, if any of these functions fail, // don't throw, error will simply have blank label. // CComPtr pMedia; HRESULT hrName; hrName = m_pRmsServer->FindCartridgeById(l_MediaToMount , &pMedia); if (hrName == S_OK) { pMedia->GetName(&pMediaName); } if ((hrName != S_OK) || ((WCHAR *)pMediaName == NULL)) { // Cannot get media name - set to blanks pMediaName = L""; } WsbThrow(hr); } WsbAffirmHr(hr); m_MountedMedia = l_MediaToMount; WsbTrace( OLESTR("Mount completed.\n") ); WsbAffirmHr(GetMediaParameters()); } }WsbCatchAndDo(hr, switch (hr){case HSM_E_STG_PL_NOT_CFGD:case HSM_E_STG_PL_INVALID: FailJob(pWorkItem); break;case RMS_E_CARTRIDGE_DISABLED: WsbLogEvent(HSM_MESSAGE_MEDIA_DISABLED, 0, NULL, pMediaName, NULL); break; default: break;} ); WsbTraceOut(OLESTR("CHsmRecallQueue::MountMedia"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::GetSource( IFsaPostIt *pFsaWorkItem, OLECHAR **pSourceString ) /*++ Routine Description: This function builds the Source file name Arguments: pFsaWorkItem - the item to be migrated pSourceString - the Source file name. Return Value: S_OK --*/ { HRESULT hr = S_OK; CComPtr pResource; CWsbStringPtr tmpString; CComPtr pSession; CWsbStringPtr path; WsbTraceIn(OLESTR("CHsmRecallQueue::GetSource"),OLESTR("")); try { // // Get the real session pointer from the IUnknown // WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); WsbAffirm(pSession != 0, E_POINTER); // First get the name of the resource from the session WsbAffirmHr(pSession->GetResource(&pResource)); WsbAffirmHr(pFsaWorkItem->GetPath(&path, 0)); tmpString.Alloc(1000); WsbAffirmHr(pResource->GetPath(&tmpString, 0)); tmpString.Append(&(path[1])); // tmpString.Prepend(OLESTR("\\\\?\\")); WsbAffirmHr(tmpString.GiveTo(pSourceString)); }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::GetSource"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::GetScanItem( IFsaPostIt *pFsaWorkItem, IFsaScanItem ** ppIFsaScanItem ) { HRESULT hr = S_OK; CWsbStringPtr path; CComPtr pSession; CComPtr pFsaResource; WsbTraceIn(OLESTR("CHsmRecallQueue::GetScanItem"),OLESTR("")); try { WsbAffirmPointer(ppIFsaScanItem); WsbAffirm(!*ppIFsaScanItem, E_INVALIDARG); WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); WsbAffirmHr(pSession->GetResource(&pFsaResource)); WsbAffirmHr(pFsaWorkItem->GetPath(&path, 0)); WsbAffirmHr(pFsaResource->FindFirst(path, pSession, ppIFsaScanItem)); }WsbCatch (hr) WsbTraceOut(OLESTR("CHsmRecallQueue::GetScanItem"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return( hr ); } DWORD HsmRecallQueueThread( void *pVoid ) /*++ --*/ { HRESULT hr; hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); hr = ((CHsmRecallQueue*) pVoid)->DoWork(); CoUninitialize(); return(hr); } HRESULT CHsmRecallQueue::SetState( IN HSM_JOB_STATE state, IN HSM_JOB_PHASE phase, IN IHsmSession * pSession ) /*++ --*/ { HRESULT hr = S_OK; BOOL bLog = TRUE; WsbTraceIn(OLESTR("CHsmRecallQueue:SetState"), OLESTR("state = <%ls>"), JobStateAsString( state ) ); try { // // Change the state and report the change to the session. Unless the current state is // failed then leave it failed. Is is necessary because when this guy fails, it will // cancel all sessions so that no more work is sent in and so we will skip any queued work. // If the current state is failed, we don't need to spit out the failed message every time, // so we send ProcessState a false fullmessage unless the state is cancelled. // WsbAffirmHr(pSession->ProcessState(phase, state, m_CurrentPath, TRUE)); }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::SetState"), OLESTR("hr = <%ls> "), WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::Cancel( IN HSM_JOB_PHASE jobPhase, IN IHsmSession *pSession ) /*++ Implements: CHsmRecallQueue::Cancel(). --*/ { HRESULT hr = S_OK; UNREFERENCED_PARAMETER(pSession); WsbTraceIn(OLESTR("CHsmRecallQueue::Cancel"), OLESTR("")); (void)SetState(HSM_JOB_STATE_CANCELLING, jobPhase, pSession); try { // // This needs to be prepended and the queue emptied out! // CComPtr pWorkItem; CComPtr pFsaWorkItem; WsbAffirmHr(m_pHsmServerCreate->CreateInstance(CLSID_CHsmRecallItem, IID_IHsmRecallItem, (void **)&pWorkItem)); // // Create the minimal postit needed to contain the session so that DoWork // can retrieve it from the work item. // TBD: make pSession a member of CHsmRecallItem, so that we don't need // to keep obtaining it via the IFsaPostIt. Also it saves us the trouble // of creating a dummy FsaPostIt here. // WsbAffirmHr(m_pHsmServerCreate->CreateInstance(CLSID_CFsaPostIt, IID_IFsaPostIt, (void **)&pFsaWorkItem)); WsbAffirmHr(pWorkItem->SetWorkType(HSM_WORK_ITEM_MOVER_CANCELLED)); WsbAffirmHr(pWorkItem->SetJobPhase(jobPhase)); WsbAffirmHr(pWorkItem->SetFsaPostIt(pFsaWorkItem)); WsbAffirmHr(pFsaWorkItem->SetSession(pSession)); // // Our work item is ready now, ship it // WsbAffirmHr(m_pWorkToDo->Prepend(pWorkItem)); }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::Cancel"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::FailJob( IHsmRecallItem *pWorkItem ) /*++ Implements: CHsmRecallQueue::FailJob(). --*/ { HRESULT hr = S_OK; CComPtr pSession; CComPtr pFsaWorkItem; HSM_JOB_PHASE jobPhase; WsbTraceIn(OLESTR("CHsmRecallQueue::FailJob"), OLESTR("")); try { // // Set our state to failed and then cancel all work // WsbAffirmHr(pWorkItem->GetFsaPostIt(&pFsaWorkItem)); WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); WsbAffirmHr(pWorkItem->GetJobPhase(&jobPhase)); WsbAffirmHr(SetState(HSM_JOB_STATE_FAILED, jobPhase, pSession)); if (pSession != 0) { WsbAffirmHr(pSession->Cancel( HSM_JOB_PHASE_ALL )); } }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::FailJob"), OLESTR("hr = <%ls>"), WsbHrAsString(hr) ); return(hr); } void CHsmRecallQueue::ReportMediaProgress( HSM_JOB_MEDIA_STATE state, HRESULT /*status*/ ) /*++ Implements: CHsmRecallQueue::ReportMediaProgress(). --*/ { HRESULT hr = S_OK; CWsbStringPtr mediaName; HSM_JOB_MEDIA_TYPE mediaType = HSM_JOB_MEDIA_TYPE_UNKNOWN; UNREFERENCED_PARAMETER(state); WsbTraceIn(OLESTR("CHsmRecallQueue::ReportMediaProgress"), OLESTR("")); // // TBD : we have to figure out a way to report media progress! // Without the session pointer this is tough.. // // Report Progress but we don't really care if it succeeds. // hr = m_pSession->ProcessMediaState(m_JobPhase, state, m_MediaName, m_MediaType, 0); WsbTraceOut(OLESTR("CHsmRecallQueue::ReportMediaProgress"), OLESTR("hr = <%ls>"), WsbHrAsString(hr) ); } HRESULT CHsmRecallQueue::GetMediaParameters( void ) /*++ Implements: CHsmRecallQueue::GetMediaParameters --*/ { HRESULT hr = S_OK; LONG rmsCartridgeType; CWsbBstrPtr barCode; WsbTraceIn(OLESTR("CHsmRecallQueue::GetMediaParameters"), OLESTR("")); try { // // Get some information about the media // WsbAffirmHr(m_pDataMover->GetLargestFreeSpace( &m_MediaFreeSpace, &m_MediaCapacity )); WsbAffirmHr(m_pRmsCartridge->GetType( &rmsCartridgeType )); WsbAffirmHr(ConvertRmsCartridgeType(rmsCartridgeType, &m_MediaType)); WsbAffirmHr(m_pRmsCartridge->GetName(&barCode)); WsbAffirmHr(CoFileTimeNow(&m_MediaUpdate)); m_MediaBarCode = barCode; }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::GetMediaParameters"), OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::DismountMedia( BOOL bNoDelay) /*++ Implements: CHsmRecallQueue::DismountMedia --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::DismountMedia"), OLESTR("")); try { if ((m_pRmsCartridge != 0) && (m_MountedMedia != GUID_NULL)) { // // End the session with the data mover. If this doesn't work, report // the problem but continue with the dismount. // // // Tell the session that we are dismounting media. Ignore any problems // with the reporting // (void )ReportMediaProgress(HSM_JOB_MEDIA_STATE_DISMOUNTING, S_OK); // // Dismount the cartridge and report progress // // !!! IMPORTANT NOTE !!! // // Must free Rms resources used before dismounting... // m_pRmsCartridge = 0; m_pDataMover = 0; DWORD dwOptions = RMS_NONE; if (bNoDelay) { dwOptions |= RMS_DISMOUNT_DEFERRED_ONLY; } hr = m_pRmsServer->DismountCartridge(m_MountedMedia, dwOptions); (void) ReportMediaProgress(HSM_JOB_MEDIA_STATE_DISMOUNTED, hr); // // Clear out the knowledge of media that was just dismounted // WsbAffirmHr(UnsetMediaInfo()); WsbAffirmHr(hr); WsbTrace( OLESTR("Dismount completed OK.\n") ); } else { WsbTrace( OLESTR("There is no media to dismount.\n") ); } }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::DismountMedia"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::ConvertRmsCartridgeType( LONG rmsCartridgeType, HSM_JOB_MEDIA_TYPE *pMediaType ) /*++ Implements: CHsmRecallQueue::ConvertRmsCartridgeType --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::ConvertRmsCartridgeType"), OLESTR("")); try { WsbAssert(0 != pMediaType, E_POINTER); switch (rmsCartridgeType) { case RmsMedia8mm: case RmsMedia4mm: case RmsMediaDLT: case RmsMediaTape: *pMediaType = HSM_JOB_MEDIA_TYPE_TAPE; break; case RmsMediaOptical: case RmsMediaMO35: case RmsMediaWORM: case RmsMediaCDR: case RmsMediaDVD: *pMediaType = HSM_JOB_MEDIA_TYPE_OPTICAL; break; case RmsMediaDisk: *pMediaType = HSM_JOB_MEDIA_TYPE_REMOVABLE_MAG; break; case RmsMediaFixed: *pMediaType = HSM_JOB_MEDIA_TYPE_FIXED_MAG; break; case RmsMediaUnknown:default: *pMediaType = HSM_JOB_MEDIA_TYPE_UNKNOWN; break; } }WsbCatch( hr ); WsbTraceOut(OLESTR("CHsmRecallQueue::ConvertRmsCartridgeType"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::MarkWorkItemAsDone(IN IHsmSession *pSession, IN HSM_JOB_PHASE jobPhase) /*++ Implements: CHsmRecallQueue::MarkWorkItemAsDone --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::MarkWorkItemAsDone"), OLESTR("")); try { // Create a work item and append it to the work queue to // indicate that the job is done CComPtr pWorkItem; CComPtr pFsaWorkItem; WsbAffirmHr(m_pHsmServerCreate->CreateInstance(CLSID_CHsmRecallItem, IID_IHsmRecallItem, (void **)&pWorkItem)); // // Create the minimal postit needed to contain the session so that DoWork // can retrieve it from the work item. // TBD: make pSession a member of CHsmRecallItem, so that we don't need // to keep obtaining it via the IFsaPostIt. Also it saves us the trouble // of creating a dummy FsaPostIt here. // WsbAffirmHr(m_pHsmServerCreate->CreateInstance(CLSID_CFsaPostIt, IID_IFsaPostIt, (void **)&pFsaWorkItem)); WsbAffirmHr(pWorkItem->SetWorkType(HSM_WORK_ITEM_FSA_DONE)); WsbAffirmHr(pWorkItem->SetJobPhase(jobPhase)); WsbAffirmHr(pWorkItem->SetFsaPostIt(pFsaWorkItem)); WsbAffirmHr(pFsaWorkItem->SetSession(pSession)); // // Our work item is ready now, ship it // WsbAffirmHr(m_pWorkToDo->Append(pWorkItem)); }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::MarkWorkItemAsDone"), OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::CheckRegistry(void) { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::CheckRegistry"), OLESTR("")); try { // Check for change to number of errors to allow before cancelling // a job WsbAffirmHr(WsbRegistryValueUlongAsString(NULL, HSM_ENGINE_REGISTRY_STRING, HSM_JOB_ABORT_CONSECUTIVE_ERRORS, &m_JobAbortMaxConsecutiveErrors)); WsbTrace(OLESTR("CHsmRecallQueue::CheckRegistry: m_JobAbortMaxConsecutiveErrors = %lu\n"), m_JobAbortMaxConsecutiveErrors); WsbAffirmHr(WsbRegistryValueUlongAsString(NULL, HSM_ENGINE_REGISTRY_STRING, HSM_JOB_ABORT_TOTAL_ERRORS, &m_JobAbortMaxTotalErrors)); WsbTrace(OLESTR("CHsmRecallQueue::CheckRegistry: m_JobAbortMaxTotalErrors = %lu\n"), m_JobAbortMaxTotalErrors); }WsbCatch( hr ); WsbTraceOut(OLESTR("CHsmRecallQueue::CheckRegistry"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return( hr ); } HRESULT CHsmRecallQueue::TranslateRmsMountHr( HRESULT rmsMountHr ) /*++ --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::TranslateRmsMountHr"),OLESTR("rms hr = <%ls>"), WsbHrAsString(rmsMountHr)); try { switch (rmsMountHr) { case S_OK: hr = S_OK; ReportMediaProgress(HSM_JOB_MEDIA_STATE_MOUNTED, hr); break; case RMS_E_MEDIASET_NOT_FOUND: if (m_RmsMediaSetId == GUID_NULL) { hr = HSM_E_STG_PL_NOT_CFGD; } else { hr = HSM_E_STG_PL_INVALID; } ReportMediaProgress(HSM_JOB_MEDIA_STATE_UNAVAILABLE, hr); break; case RMS_E_SCRATCH_NOT_FOUND: hr = HSM_E_NO_MORE_MEDIA; ReportMediaProgress(HSM_JOB_MEDIA_STATE_UNAVAILABLE, hr); break; case RMS_E_CARTRIDGE_UNAVAILABLE: case RMS_E_RESOURCE_UNAVAILABLE: case RMS_E_DRIVE_UNAVAILABLE: case RMS_E_LIBRARY_UNAVAILABLE: hr = HSM_E_MEDIA_NOT_AVAILABLE; ReportMediaProgress(HSM_JOB_MEDIA_STATE_UNAVAILABLE, hr); break; case RMS_E_CARTRIDGE_BUSY: case RMS_E_RESOURCE_BUSY: case RMS_E_DRIVE_BUSY: hr = HSM_E_MEDIA_BUSY; ReportMediaProgress(HSM_JOB_MEDIA_STATE_BUSY, hr); break; case RMS_E_CARTRIDGE_NOT_FOUND: case RMS_E_CARTRIDGE_DISABLED: case RMS_E_TIMEOUT: hr = rmsMountHr; ReportMediaProgress(HSM_JOB_MEDIA_STATE_UNAVAILABLE, hr); break; default: hr = rmsMountHr; ReportMediaProgress(HSM_JOB_MEDIA_STATE_UNAVAILABLE, hr); break; } }WsbCatch( hr ); WsbTraceOut(OLESTR("CHsmRecallQueue::TranslateRmsMountHr"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::Remove( IHsmRecallItem *pWorkItem ) /*++ Implements: IHsmFsaTskMgr::Remove --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::Remove"),OLESTR("")); try { // // Remove the item from the queue // (void)m_pWorkToDo->RemoveAndRelease(pWorkItem); }WsbCatch (hr); WsbTraceOut(OLESTR("CHsmRecallQueue::Remove"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::ChangeSysState( IN OUT HSM_SYSTEM_STATE* pSysState ) /*++ Implements: IHsmSystemState::ChangeSysState(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::ChangeSysState"), OLESTR("")); try { if (pSysState->State & HSM_STATE_SUSPEND) { // Should have already been paused via the job}else if (pSysState->State & HSM_STATE_RESUME){ // Should have already been resumed via the job}else if (pSysState->State & HSM_STATE_SHUTDOWN){ // Release the thread (we assume it has been stopped already) if (m_WorkerThread) { CloseHandle(m_WorkerThread); m_WorkerThread = 0; } if (m_pDataMover) { // // Cancel any active I/O // (void) m_pDataMover->Cancel(); } /* TBD // If Session is valid - unadvise and free session, otherwise, just try to // dismount media if it is mounted (which we don't know at this point) // Best effort dismount, no error checking so following resources will get released. if (m_pSession != 0) { EndSessions(FALSE, TRUE); } else { (void) DismountMedia(TRUE); } */ (void) DismountMedia(TRUE); } }WsbCatch(hr); WsbTraceOut(OLESTR("CHsmRecallQueue::ChangeSysState"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::EndRecallSession( IN IHsmRecallItem *pWorkItem, IN BOOL cancelled ) { HRESULT hr = S_OK; CComPtr pFsaWorkItem; DWORD stateCookie; DWORD eventCookie; ULONG refCount; WsbTraceIn(OLESTR("CHsmRecallQueue::EndRecallSession"),OLESTR("")); try { HRESULT dismountHr = S_OK; CComPtr pCPC; CComPtr pCP; CComPtr pSession; HSM_JOB_PHASE jobPhase; // // Get the session // WsbAffirmHr(pWorkItem->GetFsaPostIt(&pFsaWorkItem)); WsbAffirmHr(pFsaWorkItem->GetSession(&pSession)); WsbAffirmHr(pWorkItem->GetStateCookie(&stateCookie)); WsbAffirmHr(pWorkItem->GetEventCookie(&eventCookie)); WsbAffirmHr(pWorkItem->GetJobPhase(&jobPhase)); // // Tell the session that we don't want to be advised anymore. // try { WsbAffirmHr(pSession->QueryInterface(IID_IConnectionPointContainer, (void**) &pCPC)); WsbAffirmHr(pCPC->FindConnectionPoint(IID_IHsmSessionSinkEveryState, &pCP)); refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue before stateCookie UnAdvise: %ls \n"), WsbLongAsString((LONG) refCount)); WsbAffirmHr(pCP->Unadvise(stateCookie)); }WsbCatch( hr ); refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue after stateCookie UnAdvise: %ls \n"), WsbLongAsString((LONG) refCount)); pCPC = 0; pCP = 0; refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue before eventCookie UnAdvise: %ls \n"), WsbLongAsString((LONG) refCount)); try { WsbAffirmHr(pSession->QueryInterface(IID_IConnectionPointContainer, (void**) &pCPC)); WsbAffirmHr(pCPC->FindConnectionPoint(IID_IHsmSessionSinkEveryEvent, &pCP)); WsbAffirmHr(pCP->Unadvise(eventCookie)); }WsbCatch( hr ); refCount = (((IUnknown *) (IHsmFsaTskMgr *) this)->AddRef()) - 1; ((IUnknown *) (IHsmFsaTskMgr *)this)->Release(); WsbTrace(OLESTR("REFCOUNT for CHsmRecallQueue after eventCookie UnAdvise: %ls \n"), WsbLongAsString((LONG) refCount)); pCPC = 0; pCP = 0; WsbTrace( OLESTR("Telling Session Data mover is done\n") ); if (cancelled) { (void)SetState(HSM_JOB_STATE_DONE, jobPhase, pSession); } else { (void)SetState(HSM_JOB_STATE_CANCELLED, jobPhase, pSession); } pSession = 0; WsbAffirmHr(hr); }WsbCatch (hr); WsbTraceOut(OLESTR("CHsmRecallQueue::EndRecallSession"),OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::UnsetMediaInfo( void ) /*++ Routine Description: Sets the media data members back to their default (unset) values. Arguments: None. Return Value: S_OK: Ok. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::UnsetMediaInfo"), OLESTR("")); m_MediaId = GUID_NULL; m_MountedMedia = GUID_NULL; m_MediaType = HSM_JOB_MEDIA_TYPE_UNKNOWN; m_MediaName = OLESTR(""); m_MediaBarCode = OLESTR(""); m_MediaFreeSpace = 0; m_MediaCapacity = 0; m_MediaReadOnly = FALSE; m_MediaUpdate = WsbLLtoFT(0); WsbTraceOut(OLESTR("CHsmRecallQueue::UnsetMediaInfo"), OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return(hr); } HRESULT CHsmRecallQueue::GetMediaId (OUT GUID *mediaId) /*++ Routine Description: Gets the media id for the queue Arguments: None. Return Value: S_OK: Ok. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::GetMediaId"), OLESTR("")); *mediaId = m_MediaId; WsbTraceOut(OLESTR("CHsmRecallQueue::GetMediaId"),OLESTR("hr = <%ls>, Id = <%ls>"), WsbHrAsString(hr), WsbPtrToGuidAsString(mediaId)); return(hr); } HRESULT CHsmRecallQueue::SetMediaId (IN GUID *mediaId) /*++ Routine Description: Sets the media id for the queue Arguments: None. Return Value: S_OK: Ok. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CHsmRecallQueue::SetMediaId"), OLESTR("")); m_MediaId = *mediaId; WsbTraceOut(OLESTR("CHsmRecallQueue::SetMediaId"),OLESTR("hr = <%ls>, Id = <%ls>"), WsbHrAsString(hr), WsbPtrToGuidAsString(mediaId)); return(hr); } HRESULT CHsmRecallQueue::IsEmpty ( void ) /*++ Routine Description: Checks if the queue is empty Arguments: None. Return Value: S_OK: Queue is empty S_FALSE: Queue is non-empty --*/ { HRESULT hr; hr = m_pWorkToDo->IsEmpty(); return(hr); } HRESULT CHsmRecallQueue::FindRecallItemToCancel( IHsmRecallItem *pWorkItem, IHsmRecallItem **pWorkItemToCancel ) /*++ Routine Description: Pulls the work item that needs to be cancelled indicated by pWorkItem and returns it (by matching the pSession pointer) Arguments: None. Return Value: --*/ { CComPtr pFsaWorkItem; CComPtr pSession; CComPtr pWorkSession; HRESULT hr; ULONG index = 0; WsbTraceIn(OLESTR("CHsmRecallQueue::FindRecallItemToCancel"), OLESTR("")); pWorkItem->GetFsaPostIt(&pFsaWorkItem); pFsaWorkItem->GetSession(&pSession); pFsaWorkItem = 0; do { hr = m_pWorkToDo->At(index, IID_IHsmRecallItem, (void **)pWorkItemToCancel); if (S_OK == hr) { (*pWorkItemToCancel)->GetFsaPostIt(&pFsaWorkItem); pFsaWorkItem->GetSession(&pWorkSession); if ((pWorkItem != (*pWorkItemToCancel)) && (pSession == pWorkSession)) { WsbTrace(OLESTR("CHsmRecallQueue::FindRecallItemToCancel: Found item to cancel, pSession = %p\n"), pSession); break; } (*pWorkItemToCancel)->Release(); (*pWorkItemToCancel) = 0; pWorkSession = 0; pFsaWorkItem = 0; } index++; } while (S_OK == hr); WsbTraceOut(OLESTR("CHsmRecallQueue::FindRecallItemToCancel"), OLESTR("hr = <%ls>"),WsbHrAsString(hr)); return hr; }