/*++ © 1998 Seagate Software, Inc. All rights reserved Module Name: fsaftrcl.cpp Abstract: This class represents a filter initiated recall request that is still in-progress. Author: Chuck Bardeen [cbardeen] 12-Feb-1997 Revision History: --*/ #include "stdafx.h" #include "devioctl.h" #define WSB_TRACE_IS WSB_TRACE_BIT_FSA #include "wsb.h" #include "fsa.h" #include "job.h" #include "fsaftrcl.h" #include "rpdata.h" #include "rpio.h" static USHORT iCountFtrcl = 0; // Count of existing objects HRESULT CFsaFilterRecall::Cancel( void ) /*++ Implements: IFsaFilterRecallPriv::Cancel(). --*/ { CComPtr pClient; CComPtr pEnum; HRESULT hr = S_OK, hr2; DWORD dwStatus; WsbTraceIn(OLESTR("CFsaFilterRecall::Cancel"), OLESTR("filter Id = %I64x"), m_driversRecallId); try { WsbAffirm(!m_wasCancelled, E_UNEXPECTED); try { // // Tell the filter to fail the open of the file. // if (m_kernelCompletionSent == FALSE) { WsbAffirmHr(m_pFilterPriv->SendCancel((IFsaFilterRecallPriv *) this)); m_kernelCompletionSent = TRUE; m_wasCancelled = TRUE; } if (m_pClient != 0) { // Reporting on recall end must be synchronized with the recall start notification, // because such notification might be sent after the recall starts switch (WaitForSingleObject(m_notifyEvent, INFINITE)) { case WAIT_OBJECT_0: m_pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)); SetEvent(m_notifyEvent); break; case WAIT_FAILED: default: WsbTrace(OLESTR("CFsaFilterRecall::Cancel: WaitForSingleObject returned error %lu\n"), GetLastError()); // Notify anyway m_pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)); break; } } dwStatus = WaitForSingleObject(m_waitingClientEvent, INFINITE); // Notify on recall end no matter what the status is if (m_pWaitingClients != 0) { // // Send recall notifications to all clients waiting for // the recall to finish // hr2 = m_pWaitingClients->Enum(&pEnum); if (S_OK == hr2) { hr2 = pEnum->First(IID_IFsaFilterClient, (void**) &pClient); while (S_OK == hr2) { pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)); m_pWaitingClients->RemoveAndRelease(pClient); pClient = NULL; pEnum->Reset(); hr2 = pEnum->First(IID_IFsaFilterClient, (void**) &pClient); } } } m_waitingClientsNotified = TRUE; switch (dwStatus) { case WAIT_OBJECT_0: SetEvent(m_waitingClientEvent); break; case WAIT_FAILED: default: WsbTrace(OLESTR("CFsaFilterRecall::Cancel: WaitForSingleObject returned error %lu\n"), dwStatus); break; } // // Now get the engine to cancel it, if possible.. // if (m_pSession != 0) { WsbAffirmHr(m_pSession->Cancel(HSM_JOB_PHASE_ALL)); } } WsbCatch(hr); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::Cancel"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::CancelByDriver( void ) /*++ Implements: IFsaFilterRecallPriv::CancelByDriver(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::CancelByDriver"), OLESTR("filter Id = %I64x"), m_driversRecallId); try { WsbAffirm(!m_wasCancelled, E_UNEXPECTED); try { // // No need to tell the filter anymore - reset the flag. // m_kernelCompletionSent = TRUE; // // Now get the engine to cancel it, if possible.. // if (m_pSession != 0) { WsbAffirmHr(m_pSession->Cancel(HSM_JOB_PHASE_ALL)); } } WsbCatch(hr); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::CancelByDriver"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::CompareBy( IN FSA_RECALL_COMPARE by ) /*++ Implements: IWsbCollectable::CompareBy(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::CompareBy"), OLESTR("by = %ld"), static_cast(by)); try { m_compareBy = by; } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::CompareBy"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::CompareTo( IN IUnknown* pUnknown, OUT SHORT* pResult ) /*++ Implements: IWsbCollectable::CompareTo(). --*/ { HRESULT hr = S_OK; CComPtr pRecall; CComPtr pRecallPriv; ULONGLONG id; // WsbTraceIn(OLESTR("CFsaFilterRecall::CompareTo"), OLESTR("")); try { // Did they give us a valid item to compare to? WsbAssert(0 != pUnknown, E_POINTER); if (m_compareBy == FSA_RECALL_COMPARE_IRECALL) { // We need the IFsaFilterRecall interface to get the value of the object. WsbAffirmHr(pUnknown->QueryInterface(IID_IFsaFilterRecall, (void**) &pRecall)); // Compare the rules. hr = CompareToIRecall(pRecall, pResult); } else { // We need the IFsaFilterRecallPriv interface to get the value of the object. WsbAffirmHr(pUnknown->QueryInterface(IID_IFsaFilterRecallPriv, (void**) &pRecallPriv)); WsbAffirmHr(pRecallPriv->GetDriversRecallId(&id)); // Compare the driver id if (m_compareBy == FSA_RECALL_COMPARE_CONTEXT_ID) { hr = CompareToDriversContextId((id&0xFFFFFFFF), pResult); } else { hr = CompareToDriversRecallId(id, pResult); } } } WsbCatch(hr); // WsbTraceOut(OLESTR("CFsaFilterRecall::CompareTo"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult)); return(hr); } HRESULT CFsaFilterRecall::CompareToDriversRecallId( IN ULONGLONG id, OUT SHORT* pResult ) /*++ Implements: IFsaFilterRecall::CompareToDriversRecallId(). --*/ { HRESULT hr = S_OK; SHORT aResult; // WsbTraceIn(OLESTR("CFsaFilterRecall::CompareToDriversRecallId"), OLESTR("")); try { if (m_driversRecallId == id) aResult = 0; else aResult = 1; if (0 != aResult) { hr = S_FALSE; } if (0 != pResult) { *pResult = aResult; } } WsbCatch(hr); // WsbTraceOut(OLESTR("CFsaFilterRecall::CompareToDriversRecallId"), OLESTR("hr = <%ls>, result = <%d>"), WsbHrAsString(hr), aResult); return(hr); } HRESULT CFsaFilterRecall::CompareToDriversContextId( IN ULONGLONG id, OUT SHORT* pResult ) /*++ Implements: IFsaFilterRecall::CompareToDriversContextId(). --*/ { HRESULT hr = S_OK; SHORT aResult; // WsbTraceIn(OLESTR("CFsaFilterRecall::CompareToDriversContextId"), OLESTR("")); try { if ((m_driversRecallId & 0xFFFFFFFF) == id) aResult = 0; else aResult = 1; if (0 != aResult) { hr = S_FALSE; } if (0 != pResult) { *pResult = aResult; } } WsbCatch(hr); // WsbTraceOut(OLESTR("CFsaFilterRecall::CompareToDriversContextId"), OLESTR("hr = <%ls>, result = <%d>"), WsbHrAsString(hr), aResult); return(hr); } HRESULT CFsaFilterRecall::CompareToIdentifier( IN GUID id, OUT SHORT* pResult ) /*++ Implements: IFsaFilterRecall::CompareToIdentifier(). --*/ { HRESULT hr = S_OK; SHORT aResult; // WsbTraceIn(OLESTR("CFsaFilterRecall::CompareToIdentifier"), OLESTR("")); try { aResult = WsbSign( memcmp(&m_id, &id, sizeof(GUID)) ); if (0 != aResult) { hr = S_FALSE; } if (0 != pResult) { *pResult = aResult; } } WsbCatch(hr); // WsbTraceOut(OLESTR("CFsaFilterRecall::CompareToIdentifier"), OLESTR("hr = <%ls>, result = <%d>"), WsbHrAsString(hr), aResult); return(hr); } HRESULT CFsaFilterRecall::CompareToIRecall( IN IFsaFilterRecall* pRecall, OUT SHORT* pResult ) /*++ Implements: IFsaFilterRecall::CompareToIRecall(). --*/ { HRESULT hr = S_OK; CWsbStringPtr name; GUID id; // WsbTraceIn(OLESTR("CFsaFilterRecall::CompareToIRecall"), OLESTR("")); try { // Did they give us a valid item to compare to? WsbAssert(0 != pRecall, E_POINTER); WsbAffirmHr(pRecall->GetIdentifier(&id)); hr = CompareToIdentifier(id, pResult); } WsbCatch(hr); // WsbTraceOut(OLESTR("CFsaFilterRecall::CompareToIRecall"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult)); return(hr); } HRESULT CFsaFilterRecall::CreateLocalStream( OUT IStream **ppStream ) /*++ Implements: IFsaFilterRecall::CreateLocalStream(). --*/ { HRESULT hr = S_OK; WCHAR idString[50]; CWsbStringPtr pDrv; OLECHAR volume[64]; WsbTraceIn(OLESTR("CFsaFilterRecall::CreateLocalStream"), OLESTR("filter Id = %I64x"), m_driversRecallId); try { WsbAssert( 0 != ppStream, E_POINTER); swprintf(idString, L"%I64u", m_driversRecallId); WsbAffirmHr( CoCreateInstance( CLSID_CFilterIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)&m_pDataMover ) ); WsbAssertHr( m_pDataMover->CreateLocalStream( idString, MVR_MODE_WRITE | MVR_FLAG_HSM_SEMANTICS | MVR_FLAG_POSIX_SEMANTICS, &m_pStream ) ); // // Set the device name for the mover which is used to recall the file. // This is the RsFilter's primary device object's name to which the // the RP_PARTIAL_DATA msgs etc. will be sent // WsbAffirmHr(m_pResource->GetPath(&pDrv,0)); swprintf(volume, L"\\\\.\\%s", pDrv); // // strip trailing backslash if any // if (volume[wcslen(volume)-1] == L'\\') { volume[wcslen(volume)-1] = L'\0'; } WsbAssertHr( m_pDataMover->SetDeviceName(RS_FILTER_SYM_LINK, volume)); *ppStream = m_pStream; m_pStream.p->AddRef(); } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::CreateLocalStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::Delete( void ) /*++ Implements: IFsaFilterRecallPriv::Delete(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::Delete"), OLESTR("filter Id = %I64x"), m_driversRecallId); try { // // Tell the kernel mode filter to fail the open of the file. // if (m_kernelCompletionSent == FALSE) { WsbAffirmHr(m_pFilterPriv->SendCancel((IFsaFilterRecallPriv *) this)); m_kernelCompletionSent = TRUE; m_wasCancelled = TRUE; } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::Delete"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::FinalConstruct( void ) /*++ Implements: CComObjectRoot::FinalConstruct(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::FinalConstruct"), OLESTR("")); try { WsbAffirmHr(CWsbCollectable::FinalConstruct()); m_notifyEvent = NULL; m_waitingClientEvent = NULL; m_driversRecallId = 0; memset(&m_placeholder, 0, sizeof(FSA_PLACEHOLDER)); m_state = HSM_JOB_STATE_IDLE; m_wasCancelled = FALSE; m_kernelCompletionSent = FALSE; m_pDataMover = 0; m_pStream = 0; m_recallFlags = 0; m_compareBy = FSA_RECALL_COMPARE_IRECALL; numRefs = 0; m_waitingClientsNotified = FALSE; m_pFilterPriv = NULL; m_threadId = 0; WsbAffirmHr(CoCreateGuid(&m_id)); // Initialize notify synchronization event and waiting clients event WsbAffirmHandle((m_notifyEvent = CreateEvent(NULL, FALSE, TRUE, NULL))); WsbAffirmHandle((m_waitingClientEvent = CreateEvent(NULL, FALSE, TRUE, NULL))); // Create the waiting client collection. WsbAffirmHr(CoCreateInstance(CLSID_CWsbOrderedCollection, NULL, CLSCTX_SERVER, IID_IWsbCollection, (void**) &m_pWaitingClients)); } WsbCatch(hr); iCountFtrcl++; WsbTraceOut(OLESTR("CFsaFilterRecall::FinalConstruct"), OLESTR("hr = %ls, Count is <%d>"), WsbHrAsString(hr), iCountFtrcl); return(hr); } void CFsaFilterRecall::FinalRelease( void ) /*++ Implements: CComObjectRoot::FinalRelease(). --*/ { WsbTraceIn(OLESTR("CFsaFilterRecall::FinalRelease"), OLESTR("")); CWsbCollectable::FinalRelease(); // Free notify synchronization event and waiting client event if (m_waitingClientEvent != NULL) { CloseHandle(m_waitingClientEvent); m_waitingClientEvent = NULL; } if (m_notifyEvent != NULL) { CloseHandle(m_notifyEvent); m_notifyEvent = NULL; } iCountFtrcl--; WsbTraceOut(OLESTR("CFsaFilterRecall::FinalRelease"), OLESTR("Count is <%d>"), iCountFtrcl); } #ifdef FSA_RECALL_LEAK_TEST ULONG CFsaFilterRecall::InternalAddRef( void ) /*++ Implements: CComObjectRoot::AddRef(). --*/ { numRefs++; WsbTrace(OLESTR("CFsaFilterRecall::AddRef (%p) - Count = %u\n"), this, numRefs); return(CComObjectRoot::InternalAddRef()); } ULONG CFsaFilterRecall::InternalRelease( void ) /*++ Implements: CComObjectRoot::InternalRelease(). --*/ { WsbTrace(OLESTR("CFsaFilterRecall::Release (%p) - Count = %u\n"), this, numRefs); numRefs--; return(CComObjectRoot::InternalRelease()); } #endif HRESULT CFsaFilterRecall::GetClassID( OUT CLSID* pClsid ) /*++ Implements: IPersist::GetClassID(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::GetClassID"), OLESTR("")); try { WsbAssert(0 != pClsid, E_POINTER); *pClsid = CLSID_CFsaFilterRecallNTFS; } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::GetClassID"), OLESTR("hr = <%ls>, CLSID = <%ls>"), WsbHrAsString(hr), WsbGuidAsString(*pClsid)); return(hr); } HRESULT CFsaFilterRecall::GetClient( OUT IFsaFilterClient** ppClient ) /*++ Implements: IFsaFilterRecallPriv::GetClient(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != ppClient, E_POINTER); *ppClient = m_pClient; if (m_pClient != 0) { m_pClient.p->AddRef(); } } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetRecallFlags( OUT ULONG *pFlags ) /*++ Implements: IFsaFilterRecall::GetRecallFlags() --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::GetRecallFlags"), OLESTR("")); try { WsbAssert( 0 != pFlags, E_POINTER); *pFlags = m_recallFlags; } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::GetRecallFlags"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::GetStream( OUT IStream **ppStream ) /*++ Implements: IFsaFilterRecall::GetStream() --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::GetStream"), OLESTR("")); try { WsbAssert( 0 != ppStream, E_POINTER); if ((m_mode & FILE_OPEN_NO_RECALL) && (m_pStream != 0)) { *ppStream = m_pStream; m_pStream.p->AddRef(); } else { *ppStream = 0; hr = WSB_E_NOTFOUND; } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::GetStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::GetDriversRecallId( OUT ULONGLONG* pId ) /*++ Implements: IFsaFilterRecallPriv::GetDriversRecallId(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pId, E_POINTER); *pId = m_driversRecallId; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetIdentifier( OUT GUID* pId ) /*++ Implements: IFsaFilterRecall::GetIdentifier(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pId, E_POINTER); *pId = m_id; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetMode( OUT ULONG* pMode ) /*++ Implements: IFsaFilterRecall::GetMode(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pMode, E_POINTER); *pMode = m_mode; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetOffset( OUT LONGLONG* pOffset ) /*++ Implements: IFsaFilterRecall::GetOffset(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pOffset, E_POINTER); *pOffset = m_offset; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetPath( OUT OLECHAR** pName, IN ULONG bufferSize ) /*++ Implements: IFsaFilterRecall::GetPath(). --*/ { HRESULT hr = S_OK; CWsbStringPtr tmpString; try { WsbAssert(0 != pName, E_POINTER); WsbAffirmHr(tmpString.TakeFrom(*pName, bufferSize)); try { WsbAffirmHr(m_pResource->GetUncPath(&tmpString, 0)); WsbAffirmHr(tmpString.Append(m_path)); } WsbCatch(hr); WsbAffirmHr(tmpString.GiveTo(pName)); } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetPlaceholder( OUT FSA_PLACEHOLDER* pPlaceholder ) /*++ Implements: IFsaFilterRecallPriv::GetPlaceholder(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pPlaceholder, E_POINTER); *pPlaceholder = m_placeholder; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetResource( OUT IFsaResource** ppResource ) /*++ Implements: IFsaFilterRecall::GetResource(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != ppResource, E_POINTER); *ppResource = m_pResource; m_pResource.p->AddRef(); } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetSession( OUT IHsmSession** ppSession ) /*++ Implements: IFsaFilterRecall::GetSession(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != ppSession, E_POINTER); *ppSession = m_pSession; m_pSession.p->AddRef(); } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetSize( OUT LONGLONG* pSize ) /*++ Implements: IFsaFilterRecall::GetSize(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pSize, E_POINTER); *pSize = m_size; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetSizeMax( OUT ULARGE_INTEGER* pSize ) /*++ Implements: IPersistStream::GetSizeMax(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::GetSizeMax"), OLESTR("")); try { WsbAssert(0 != pSize, E_POINTER); pSize->QuadPart = 0; // WE don't need to persist these. hr = E_NOTIMPL; } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::GetSizeMax"), OLESTR("hr = <%ls>, Size = <%ls>"), WsbHrAsString(hr), WsbPtrToUliAsString(pSize)); return(hr); } HRESULT CFsaFilterRecall::GetState( OUT HSM_JOB_STATE* pState ) /*++ Implements: IFsaFilterRecall::GetState(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pState, E_POINTER); *pState = m_state; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::GetUserName( OUT OLECHAR** pName, IN ULONG bufferSize ) /*++ Implements: IFsaFilterRecall::GetUserName(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != pName, E_POINTER); if (m_pClient != 0) { WsbAffirmHr(m_pClient->GetUserName(pName, bufferSize)); } else { hr = WSB_E_NOTFOUND; } } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::HasCompleted( HRESULT resultHr ) /*++ Implements: IFsaFilterRecall::HasCompleted(). --*/ { HRESULT hr = S_OK, hr2 = S_OK; CComPtr pClient; CComPtr pEnum; FILETIME now; BOOL bSendNotify = TRUE; DWORD dwStatus; WsbTraceIn(OLESTR("CFsaFilterRecall::HasCompleted"), OLESTR("filter Id = %I64x, recall hr = <%ls>"), m_driversRecallId, WsbHrAsString(resultHr)); try { // The job is complete, let the kernel mode filter know what happened. GetSystemTimeAsFileTime(&now); if (m_pClient != 0) { m_pClient->SetLastRecallTime(now); // Not fatal if this fails } if (m_kernelCompletionSent == FALSE) { WsbAffirmHr(m_pFilterPriv->SendComplete((IFsaFilterRecallPriv *) this, resultHr)); m_kernelCompletionSent = TRUE; } if (m_pClient != 0) { // Reporting on recall end must be synchronized with the recall start notification, // because such notification might be sent after the recall starts switch (WaitForSingleObject(m_notifyEvent, INFINITE)) { case WAIT_OBJECT_0: // Send recall notifications to the client that initiated the recall m_pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, resultHr); SetEvent(m_notifyEvent); break; case WAIT_FAILED: default: WsbTrace(OLESTR("CFsaFilterRecall::HasCompleted: WaitForSingleObject returned error %lu\n"), GetLastError()); // Notify anyway m_pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, resultHr); break; } bSendNotify = FALSE; } dwStatus = WaitForSingleObject(m_waitingClientEvent, INFINITE); // Notify on recall end no matter what the status is if (m_pWaitingClients != 0) { // // Send recall notifications to all clients waiting for the recall // to finish // hr2 = m_pWaitingClients->Enum(&pEnum); if (S_OK == hr2) { hr2 = pEnum->First(IID_IFsaFilterClient, (void**) &pClient); while (S_OK == hr2) { pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, resultHr); m_pWaitingClients->RemoveAndRelease(pClient); pClient = NULL; pEnum->Reset(); hr2 = pEnum->First(IID_IFsaFilterClient, (void**) &pClient); } } } m_waitingClientsNotified = TRUE; switch (dwStatus) { case WAIT_OBJECT_0: SetEvent(m_waitingClientEvent); break; case WAIT_FAILED: default: WsbTrace(OLESTR("CFsaFilterRecall::HasCompleted: WaitForSingleObject returned error %lu\n"), dwStatus); break; } // // Detach the data mover stream // if (m_pDataMover != 0) { WsbAffirmHr( m_pDataMover->CloseStream() ); } } WsbCatchAndDo(hr, if ((m_pClient != 0) && bSendNotify) { m_pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, resultHr); bSendNotify = FALSE; } ); WsbTraceOut(OLESTR("CFsaFilterRecall::HasCompleted"), OLESTR("filter Id = %I64x, sent = <%ls>, hr = <%ls>"), m_driversRecallId, WsbBoolAsString(m_kernelCompletionSent), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::CheckRecallLimit( IN DWORD minRecallInterval, IN DWORD maxRecalls, IN BOOLEAN exemptAdmin ) /*++ Implements: IFsaFilterRecall::CheckRecallLimit(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::CheckRecallLimit"), OLESTR("filter Id = %I64x"), m_driversRecallId); try { // Check the limit if we are not file open no recall if (!(m_mode & FILE_OPEN_NO_RECALL) && (m_pClient != NULL)) { WsbAffirmHr(m_pClient->CheckRecallLimit(minRecallInterval, maxRecalls, exemptAdmin)); } } WsbCatch(hr); // // Commenting the following out: we are reverting back to // denial of service when we hit the recall limit, not trunc-on-close // // If we hit the recall limit then we start to truncate on close. // // if (hr == FSA_E_HIT_RECALL_LIMIT) { // m_recallFlags |= RP_RECALL_ACTION_TRUNCATE; // } WsbTraceOut(OLESTR("CFsaFilterRecall::CheckRecallLimit"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::Init( IN IFsaFilterClient* pClient, IN ULONGLONG DriversRecallId, IN IFsaResource* pResource, IN OLECHAR* path, IN LONGLONG fileId, IN LONGLONG offset, IN LONGLONG size, IN ULONG mode, IN FSA_PLACEHOLDER* pPlaceholder, IN IFsaFilterPriv *pFilterPriv ) /*++ Implements: IFsaFilterRecallPriv::Init(). --*/ { HRESULT hr = S_OK; FILETIME now; CComPtr pResourcePriv; WsbTraceIn(OLESTR("CFsaFilterRecall::Init"), OLESTR("filter ID = %I64x, offset = %I64u, size = %I64u"), DriversRecallId, offset, size); try { m_pClient = pClient; m_driversRecallId = DriversRecallId; m_pResource = pResource; m_placeholder = *pPlaceholder; m_pFilterPriv = pFilterPriv; m_path = path; m_mode = mode; m_fileId = fileId; GetSystemTimeAsFileTime(&m_startTime); m_offset = offset; m_size = size; m_isDirty = TRUE; WsbAssert(m_path != 0, E_UNEXPECTED); // // Get the recall started with the engine // Start a session and ask it to advise us of state changes. // Tell the resource object that we got an open. // hr = S_OK; } WsbCatchAndDo(hr, // // Something failed - send the kernel completion if it has not been sent already. // GetSystemTimeAsFileTime(&now); if (m_pClient != 0) { m_pClient->SetLastRecallTime(now); } if (m_kernelCompletionSent == FALSE) { m_pFilterPriv->SendComplete((IFsaFilterRecallPriv *) this, hr); m_kernelCompletionSent = TRUE; } else { WsbLogEvent(FSA_MESSAGE_RECALL_FAILED, 0, NULL, (OLECHAR*) WsbAbbreviatePath(path, 120), WsbHrAsString(hr), NULL); } if (m_pClient != 0) { m_pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, E_FAIL); // Not fatal if this fails } ); WsbTraceOut(OLESTR("CFsaFilterRecall::Init"), OLESTR("%ls"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::Load( IN IStream* pStream ) /*++ Implements: IPersistStream::Load(). --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CFsaFilterRecall::Load"), OLESTR("")); try { WsbAssert(0 != pStream, E_POINTER); // No persistence. hr = E_NOTIMPL; } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::Load"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::LogComplete( IN HRESULT result ) /*++ Implements: IFsaFilterRecallPriv:LogComplete(HRESULT result) --*/ { HRESULT hr = S_OK; FILETIME completeTime; LONGLONG recallTime; WsbTraceIn(OLESTR("CFsaFilterRecall::LogComplete"), OLESTR("filter Id = %I64x"), m_driversRecallId); try { // Calculate the time it took for this recall to complete GetSystemTimeAsFileTime(&completeTime); recallTime = WsbFTtoLL(WsbFtSubFt(completeTime, m_startTime)); // If over 10 minutes then show time in minutes otherwise show in seconds if (recallTime >= (WSB_FT_TICKS_PER_MINUTE * (LONGLONG) 10)) { recallTime = recallTime / WSB_FT_TICKS_PER_MINUTE; WsbTrace(OLESTR("CFsaFilterRecall::LogComplete Recall of %ws completed in %I64u minutes. (%ws)\n"), WsbAbbreviatePath(m_path, 120), recallTime, WsbHrAsString(result)); WsbLogEvent(FSA_MESSAGE_RECALL_TIMING_MINUTES, 0, NULL, WsbAbbreviatePath(m_path, 120), WsbLonglongAsString(recallTime), WsbHrAsString(result), NULL); } else { recallTime = recallTime / WSB_FT_TICKS_PER_SECOND; WsbTrace(OLESTR("CFsaFilterRecall::LogComplete Recall of %ws completed in %I64u seconds. (%ws)\n"), WsbAbbreviatePath(m_path, 120), recallTime, WsbHrAsString(result)); WsbLogEvent(FSA_MESSAGE_RECALL_TIMING_SECONDS, 0, NULL, WsbAbbreviatePath(m_path, 120), WsbLonglongAsString(recallTime), WsbHrAsString(result), NULL); } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::LogComplete"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::Save( IN IStream* pStream, IN BOOL clearDirty ) /*++ Implements: IPersistStream::Save(). --*/ { HRESULT hr = S_OK; CComPtr pPersistStream; WsbTraceIn(OLESTR("CFsaFilterRecall::Save"), OLESTR("clearDirty = <%ls>"), WsbBoolAsString(clearDirty)); try { WsbAssert(0 != pStream, E_POINTER); // No persistence. hr = E_NOTIMPL; // If we got it saved and we were asked to clear the dirty bit, then // do so now. if (clearDirty) { m_isDirty = FALSE; } } WsbCatch(hr); WsbTraceOut(OLESTR("CFsaFilterRecall::Save"), OLESTR("hr = <%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::SetDriversRecallId( IN ULONGLONG pId ) /*++ Implements: IFsaFilterRecallPriv::SetDriversRecallId(). --*/ { HRESULT hr = S_OK; try { m_driversRecallId = pId; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::SetThreadId( IN DWORD id ) /*++ Implements: IFsaFilterRecallPriv::SetThreadId(). --*/ { HRESULT hr = S_OK; try { m_threadId = id; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::SetIdentifier( IN GUID id ) /*++ Implements: IFsaFilterRecallPriv::SetIdentifier(). --*/ { HRESULT hr = S_OK; m_id = id; m_isDirty = TRUE; return(hr); } HRESULT CFsaFilterRecall::StartRecall( IN ULONGLONG offset, IN ULONGLONG size ) /*++ Implements: IFsaFilterRecallPriv::StartRecall(). --*/ { HRESULT hr = S_OK; FILETIME now; CComPtr pResourcePriv; CWsbStringPtr sessionName; ULONG tryLoop; BOOL bSentNotify = FALSE; WsbTraceIn(OLESTR("CFsaFilterRecall::StartRecall"), OLESTR("filter Id = %I64x"), m_driversRecallId); try { m_offset = offset; m_size = size; if (m_mode & FILE_OPEN_NO_RECALL) { if (m_offset >= m_placeholder.dataStreamSize) { // // Read beyond the end of file // hr = STATUS_END_OF_FILE; WsbAffirmHr(hr); } else if ( (m_offset + m_size) > (m_placeholder.dataStreamStart + m_placeholder.dataStreamSize) ) { // // They are asking for more than we have - adjust the read size // m_size -= (m_offset + m_size) - (m_placeholder.dataStreamStart + m_placeholder.dataStreamSize); } } m_isDirty = TRUE; WsbAssert(m_path != 0, E_UNEXPECTED); // // Get the recall started with the engine // Start a session and ask it to advise us of state changes. // Tell the resource object that we got an open. // WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: BeginSession\n")); // Get the string that we are using to describe the session. WsbAffirmHr(sessionName.LoadFromRsc(_Module.m_hInst, IDS_FSA_RECALL_NAME)); WsbAffirmHr(m_pResource->BeginSession(sessionName, HSM_JOB_LOG_ITEMMOSTFAIL | HSM_JOB_LOG_HR, 1, 1, &m_pSession)); WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: Session is setup.\n")); WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: Notify the client that the recall started.\n")); if (m_pClient != 0) { hr = m_pClient->SendRecallInfo((IFsaFilterRecall *) this, TRUE, S_OK); // Not fatal if this fails if (! SUCCEEDED(hr)) { WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: SendNotify failed with %ls.\n"), WsbHrAsString(hr)); } else { if (hr != S_OK) { WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: SendNotify returned %ls.\n"), WsbHrAsString(hr)); } bSentNotify = TRUE; } } hr = S_OK; // // Tell the resource to send the job to the engine. // WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: Calling FilterSawOpen.\n")); WsbAffirmHr(m_pResource->QueryInterface(IID_IFsaResourcePriv, (void**) &pResourcePriv)); if (m_mode & FILE_OPEN_NO_RECALL) { WsbAffirmHr(pResourcePriv->FilterSawOpen(m_pSession, (IFsaFilterRecall*) this, m_path, m_fileId, offset, size, &m_placeholder, m_mode, FSA_RESULT_ACTION_NORECALL, m_threadId)); } else { WsbAffirmHr(pResourcePriv->FilterSawOpen(m_pSession, (IFsaFilterRecall*) this, m_path, m_fileId, offset, size, &m_placeholder, m_mode, FSA_RESULT_ACTION_OPEN, m_threadId)); } // // The work is now complete - terminate the session. // WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: End Session.\n")); WsbAffirmHr(m_pResource->EndSession(m_pSession)); // // Try the notification again if we have not sent it yet. // On the first recall from a remote client the identification usually does not // happen in time for the first attempt so we try again here. // We will try 5 times with a .1 second delay between. // WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: m_pClient = %x sent = %u.\n"), m_pClient, bSentNotify); if ((m_pClient != 0) && (!bSentNotify)) { tryLoop = 5; while ((tryLoop != 0) &&( !bSentNotify)) { // Reporting here is done after the recall is started. // Therefore, it must be synchronized with the recall end notification switch (WaitForSingleObject(m_notifyEvent, INFINITE)) { case WAIT_OBJECT_0: // Check if need to report (if recall did not end yet) if (m_kernelCompletionSent == FALSE) { // Recall end was not sent yet hr = m_pClient->SendRecallInfo((IFsaFilterRecall *) this, TRUE, S_OK); // Not fatal if this fails } SetEvent(m_notifyEvent); break; case WAIT_FAILED: default: WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: WaitForSingleObject returned error %lu\n"), GetLastError()); // Just get out without notifying hr = S_OK; break; } if (! SUCCEEDED(hr)) { WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: Retried notify failed with %ls.\n"), WsbHrAsString(hr)); if (tryLoop != 1) { Sleep(100); // Sleep .1 sec and try again } } else { if (hr != S_OK) WsbTrace(OLESTR("CFsaFilterRecall::StartRecall: Retried notify returned %ls.\n"), WsbHrAsString(hr)); bSentNotify = TRUE; } tryLoop--; } hr = S_OK; } } WsbCatchAndDo(hr, // // Something failed - send the kernel completion if it has not been sent already. // GetSystemTimeAsFileTime(&now); if (m_pClient != 0) { m_pClient->SetLastRecallTime(now); } if (m_kernelCompletionSent == FALSE) { m_pFilterPriv->SendComplete((IFsaFilterRecallPriv *) this, hr); m_kernelCompletionSent = TRUE; } else { // // STATUS_END_OF_FILE is not really an error - it just means they tried to read past the end - some apps do this and expect // this status to tell them when to stop reading. // if (hr != STATUS_END_OF_FILE) { WsbLogEvent(FSA_MESSAGE_RECALL_FAILED, 0, NULL, (OLECHAR*) WsbAbbreviatePath(m_path, 120), WsbHrAsString(hr), NULL); } } if (m_pClient != 0) { m_pClient->SendRecallInfo((IFsaFilterRecall *) this, FALSE, E_FAIL); // Not fatal if this fails } ); WsbTraceOut(OLESTR("CFsaFilterRecall::StartRecall"), OLESTR("%ls"), WsbHrAsString(hr)); return(hr); } HRESULT CFsaFilterRecall::Test( USHORT* passed, USHORT* failed ) /*++ Implements: IWsbTestable::Test(). --*/ { HRESULT hr = S_OK; try { WsbAssert(0 != passed, E_POINTER); WsbAssert(0 != failed, E_POINTER); *passed = 0; *failed = 0; } WsbCatch(hr); return(hr); } HRESULT CFsaFilterRecall::WasCancelled( void ) /*++ Implements: IFsaFilterRecall::WasCancelled(). --*/ { HRESULT hr = S_OK; if (!m_wasCancelled) { hr = S_FALSE; } return(hr); } HRESULT CFsaFilterRecall::AddClient( IFsaFilterClient *pWaitingClient ) /*++ Implements: IFsaFilterRecall::AddClient --*/ { HRESULT hr = E_FAIL; switch (WaitForSingleObject(m_waitingClientEvent, INFINITE)) { case WAIT_OBJECT_0: if ((!m_waitingClientsNotified) && (m_pWaitingClients != 0)) { hr = m_pWaitingClients->Add(pWaitingClient); if (hr == S_OK) { // Notify client only if it was added successfully to the collection hr = pWaitingClient->SendRecallInfo((IFsaFilterRecall *) this, TRUE, S_OK); // Not fatal if this fails if (hr != S_OK) { // Note that S_FALSE is an "expected failure" but we still want to trace this WsbTrace(OLESTR("CFsaFilterRecall::AddClient: SendNotify for start returned %ls.\n"), WsbHrAsString(hr)); } } } SetEvent(m_waitingClientEvent); break; case WAIT_FAILED: default: DWORD dwErr = GetLastError(); WsbTrace(OLESTR("CFsaFilterRecall::AddClient: WaitForSingleObject returned error %lu\n"), dwErr); // Don't add waiting client hr = HRESULT_FROM_WIN32(dwErr); break; } return(hr); }