Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1728 lines
57 KiB

/*++
© 1998 Seagate Software, Inc. All rights reserved.
Module Name:
fsatrunc.cpp
Abstract:
This class handles the automatic truncation of files that have already
been premigrated.
Author:
Chuck Bardeen [cbardeen] 20-Feb-1997
Revision History:
--*/
#include "stdafx.h"
#define WSB_TRACE_IS WSB_TRACE_BIT_FSA
#include "wsb.h"
#include "fsa.h"
#include "fsaprem.h"
#include "fsarcvy.h"
#include "fsasrvr.h"
#include "fsatrunc.h"
#include "job.h"
#define DEFAULT_MAX_FILES_PER_RUN 10000
#define DEFAULT_RUN_INTERVAL (15 * 60 * 1000) // 15 minutes in milliseconds
#define STRINGIZE(_str) (OLESTR( #_str ))
#define RETURN_STRINGIZED_CASE(_case) \
case _case: \
return ( STRINGIZE( _case ) );
static const OLECHAR *
FsaStateAsString (
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") );
}
}
static const OLECHAR *
FsaEventAsString (
IN HSM_JOB_EVENT event
)
/*++
Routine Description:
Gives back a static string representing the connection event.
Arguments:
event - the event to return a string for.
Return Value:
NULL - invalid event passed in.
Otherwise, a valid char *.
--*/
{
//
// Do the Switch
//
switch ( event ) {
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_CANCEL );
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_FAIL );
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_LOWER_PRIORITY );
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_PAUSE );
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_RAISE_PRIORITY );
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_RESUME );
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_START );
RETURN_STRINGIZED_CASE( HSM_JOB_EVENT_SUSPEND );
default:
return ( OLESTR("Invalid Value") );
}
}
static const OLECHAR *
FsaSortOrderAsString (
IN FSA_PREMIGRATED_SORT_ORDER SortOrder
)
/*++
Routine Description:
Gives back a static string representing the connection SortOrder.
Arguments:
SortOrder - the SortOrder to return a string for.
Return Value:
NULL - invalid SortOrder passed in.
Otherwise, a valid char *.
--*/
{
//
// Do the Switch
//
switch ( SortOrder ) {
RETURN_STRINGIZED_CASE( FSA_SORT_PL_BY_ACCESS_TIME );
RETURN_STRINGIZED_CASE( FSA_SORT_PL_BY_SIZE );
RETURN_STRINGIZED_CASE( FSA_SORT_PL_BY_PATH_NAME );
RETURN_STRINGIZED_CASE( FSA_SORT_PL_BY_SIZE_AND_TIME );
default:
return ( OLESTR("Invalid Value") );
}
}
DWORD FsaStartTruncator(
void* pVoid
)
/*++
--*/
{
return(((CFsaTruncator*) pVoid)->StartScan());
}
HRESULT
CFsaTruncator::Cancel(
HSM_JOB_EVENT event
)
/*++
Implements:
IFsaTruncator::Cancel().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::Cancel"), OLESTR("event = <%ls>"), FsaEventAsString( event ));
// Lock this object to avoid having the state change between testing its value
// and setting it to a new value
Lock();
try {
// If we have started, but haven't finished, then change the state of the job. The thread
// will exit on it's own.
if ((HSM_JOB_STATE_IDLE != m_state) &&
(HSM_JOB_STATE_DONE != m_state) &&
(HSM_JOB_STATE_FAILED != m_state) &&
(HSM_JOB_STATE_CANCELLED != m_state)) {
if (HSM_JOB_EVENT_CANCEL == event) {
WsbAffirmHr(SetState(HSM_JOB_STATE_CANCELLED));
} else if (HSM_JOB_EVENT_SUSPEND == event) {
WsbAffirmHr(SetState(HSM_JOB_STATE_SUSPENDED));
} else if (HSM_JOB_EVENT_FAIL == event) {
WsbAffirmHr(SetState(HSM_JOB_STATE_FAILED));
} else {
WsbAssert(FALSE, E_UNEXPECTED);
}
}
} WsbCatch(hr);
Unlock();
WsbTraceOut(OLESTR("CFsaTruncator::Cancel"), OLESTR("hr = <%ls> m_state = <%ls>"), WsbHrAsString(hr), FsaStateAsString( m_state ) );
return(hr);
}
HRESULT
CFsaTruncator::FinalConstruct(
void
)
/*++
Implements:
CComObjectRoot::FinalConstruct().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::FinalConstruct"), OLESTR(""));
try {
WsbAffirmHr(CWsbPersistStream::FinalConstruct());
m_state = HSM_JOB_STATE_IDLE;
m_priority = HSM_JOB_PRIORITY_NORMAL;
m_threadHandle = 0;
m_threadId = 0;
m_threadHr = S_OK;
m_maxFiles = DEFAULT_MAX_FILES_PER_RUN;
m_runInterval = DEFAULT_RUN_INTERVAL;
m_runId = 0;
m_subRunId = 0;
m_pSession = 0;
m_SortOrder = FSA_SORT_PL_BY_ACCESS_TIME;
m_keepRecallTime = WsbLLtoFT(WSB_FT_TICKS_PER_MINUTE);
m_event = 0;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::FinalConstruct"), OLESTR(""));
return(hr);
}
HRESULT
CFsaTruncator::FinalRelease(
void
)
/*++
Implements:
CComObjectRoot::FinalRelease().
--*/
{
HRESULT hr = S_OK;
HSM_SYSTEM_STATE SysState;
WsbTraceIn(OLESTR("CFsaTruncator::FinalRelease"), OLESTR(""));
SysState.State = HSM_STATE_SHUTDOWN;
ChangeSysState(&SysState);
CWsbPersistStream::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_currentPath.Free();
WsbTraceOut(OLESTR("CFsaTruncator::FinalRelease"), OLESTR(""));
return(hr);
}
HRESULT
CFsaTruncator::GetClassID(
OUT CLSID* pClsid
)
/*++
Implements:
IPersist::GetClassID().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::GetClassID"), OLESTR(""));
try {
WsbAssert(0 != pClsid, E_POINTER);
*pClsid = CLSID_CFsaTruncatorNTFS;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::GetClassID"), OLESTR("hr = <%ls>, CLSID = <%ls>"), WsbHrAsString(hr), WsbGuidAsString(*pClsid));
return(hr);
}
HRESULT
CFsaTruncator::GetKeepRecallTime(
OUT FILETIME* pTime
)
/*++
Implements:
IFsaTruncator::GetKeepRecallTime().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::GetKeepRecallTime"), OLESTR(""));
try {
WsbAssert(0 != pTime, E_POINTER);
*pTime = m_keepRecallTime;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::GetKeepRecallTime"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::GetMaxFilesPerRun(
OUT LONGLONG* pMaxFiles
)
/*++
Implements:
IFsaTruncator::GetMaxFilesPerRun().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::GetMaxFilesPerRun"), OLESTR(""));
try {
WsbAssert(0 != pMaxFiles, E_POINTER);
*pMaxFiles = m_maxFiles;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::GetMaxFilesPerRun"), OLESTR("hr = <%ls> maxFiles = <%ls>"), WsbHrAsString(hr), WsbLonglongAsString( *pMaxFiles ) );
return(hr);
}
HRESULT
CFsaTruncator::GetPremigratedSortOrder(
OUT FSA_PREMIGRATED_SORT_ORDER* pSortOrder
)
/*++
Implements:
IFsaTruncator::GetPremigratedSortOrder().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::GetPremigratedSortOrder"), OLESTR(""));
try {
WsbAssert(0 != pSortOrder, E_POINTER);
*pSortOrder = m_SortOrder;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::GetPremigratedSortOrder"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::GetRunInterval(
OUT ULONG* pMilliseconds
)
/*++
Implements:
IFsaTruncator::GetRunInterval().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::GetRunInterval"), OLESTR(""));
try {
WsbAssert(0 != pMilliseconds, E_POINTER);
*pMilliseconds = m_runInterval;
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::GetRunInterval"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::GetSession(
OUT IHsmSession** ppSession
)
/*++
Implements:
IFsaTruncator::GetSession().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::GetSession"), OLESTR(""));
try {
WsbAssert(0 != ppSession, E_POINTER);
*ppSession = m_pSession;
if (m_pSession != 0) {
m_pSession.p->AddRef();
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::GetSession"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::GetSizeMax(
OUT ULARGE_INTEGER* pSize
)
/*++
Implements:
IPersistStream::GetSizeMax().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::GetSizeMax"), OLESTR(""));
try {
WsbAssert(0 != pSize, E_POINTER);
// Determine the size for a rule with no criteria.
pSize->QuadPart = WsbPersistSizeOf(LONGLONG) + 3 * WsbPersistSizeOf(ULONG) + WsbPersistSizeOf(FILETIME);
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::GetSizeMax"), OLESTR("hr = <%ls>, Size = <%ls>"), WsbHrAsString(hr), WsbPtrToUliAsString(pSize));
return(hr);
}
HRESULT
CFsaTruncator::KickStart(
void
)
/*++
Implements:
IFsaTruncator:KickStart
Data was just moved for this volume - wake up the truncator thread in case we need space.
--*/
{
HRESULT hr = S_OK;
CComPtr<IFsaResource> pResource;
ULONG freeLevel;
ULONG hsmLevel;
WsbTraceIn(OLESTR("CFsaTruncator::KickStart"), OLESTR(""));
try {
if (m_pSession) {
WsbAffirmHr(m_pSession->GetResource(&pResource));
// If the truncator is running and the resource does not have enough free space
// check to see if the resource is over the threshold and truncation is needed.
WsbAffirmHr(pResource->GetHsmLevel(&hsmLevel));
WsbAffirmHr(pResource->GetFreeLevel(&freeLevel));
if (freeLevel < hsmLevel) {
WsbTrace(OLESTR("CFsaTruncator::KickStarting truncator.\n"));
SetEvent(m_event);
}
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::KickStart"), OLESTR("hr = <%ls>>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::Load(
IN IStream* pStream
)
/*++
Implements:
IPersistStream::Load().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::Load"), OLESTR(""));
try {
USHORT us_tmp;
ULONG ul_tmp;
WsbAssert(0 != pStream, E_POINTER);
// Do the easy stuff, but make sure that this order matches the order
// in the save method.
WsbAffirmHr(WsbLoadFromStream(pStream, &ul_tmp));
m_priority = static_cast<HSM_JOB_PRIORITY>(ul_tmp);
WsbAffirmHr(WsbLoadFromStream(pStream, &m_maxFiles));
WsbAffirmHr(WsbLoadFromStream(pStream, &m_runInterval));
WsbAffirmHr(WsbLoadFromStream(pStream, &m_runId));
WsbAffirmHr(WsbLoadFromStream(pStream, &m_keepRecallTime));
WsbAffirmHr(WsbLoadFromStream(pStream, &us_tmp));
m_SortOrder = static_cast<FSA_PREMIGRATED_SORT_ORDER>(us_tmp);
// Check to see if values for maxFiles and runInterval are specified in the registry.
// If so, use these values instead of the ones stored.
{
DWORD sizeGot;
CWsbStringPtr tmpString;
WsbAffirmHr(tmpString.Alloc(256));
if (SUCCEEDED(WsbGetRegistryValueString(NULL, FSA_REGISTRY_PARMS, FSA_REGISTRY_TRUNCATOR_INTERVAL, tmpString, 256, &sizeGot))) {
m_runInterval = 1000 * wcstoul(tmpString, NULL, 10);
} else {
m_runInterval = DEFAULT_RUN_INTERVAL;
}
if (SUCCEEDED(WsbGetRegistryValueString(NULL, FSA_REGISTRY_PARMS, FSA_REGISTRY_TRUNCATOR_FILES, tmpString, 256, &sizeGot))) {
m_maxFiles = (LONGLONG) wcstoul(tmpString, NULL, 10);
} else {
m_maxFiles = DEFAULT_MAX_FILES_PER_RUN;
}
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::Load"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::LowerPriority(
void
)
/*++
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::LowerPriority"), OLESTR(""));
try {
WsbAssert(0 != m_threadHandle, E_UNEXPECTED);
WsbAssert(m_pSession != 0, E_UNEXPECTED);
switch(m_priority) {
case HSM_JOB_PRIORITY_IDLE:
WsbAffirm(FALSE, E_UNEXPECTED);
break;
case HSM_JOB_PRIORITY_LOWEST:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_IDLE));
m_priority = HSM_JOB_PRIORITY_IDLE;
break;
case HSM_JOB_PRIORITY_LOW:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_LOWEST));
m_priority = HSM_JOB_PRIORITY_LOWEST;
break;
case HSM_JOB_PRIORITY_NORMAL:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_BELOW_NORMAL));
m_priority = HSM_JOB_PRIORITY_LOW;
break;
case HSM_JOB_PRIORITY_HIGH:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_NORMAL));
m_priority = HSM_JOB_PRIORITY_NORMAL;
break;
case HSM_JOB_PRIORITY_HIGHEST:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_ABOVE_NORMAL));
m_priority = HSM_JOB_PRIORITY_HIGH;
break;
default:
case HSM_JOB_PRIORITY_CRITICAL:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_HIGHEST));
m_priority = HSM_JOB_PRIORITY_HIGHEST;
break;
}
WsbAffirmHr(m_pSession->ProcessPriority(HSM_JOB_PHASE_SCAN, m_priority));
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::LowerPriority"), OLESTR("hr = <%ls>"), WsbHrAsString(hr) );
return(hr);
}
HRESULT
CFsaTruncator::Pause(
void
)
/*++
Implements:
IFsaTruncator::Pause().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::Pause"), OLESTR("state = %ls"),
FsaStateAsString(m_state));
// Lock this object to avoid having the state change between testing its value
// and setting it to a new value
Lock();
try {
// If we are running, then suspend the thread.
WsbAssert(HSM_JOB_STATE_ACTIVE == m_state, E_UNEXPECTED);
// Set the state & the active thread will not do any work
WsbAffirmHr(SetState(HSM_JOB_STATE_PAUSING));
// We would like to wait until the thread is really inactive, but that's
// hard to tell because it could be in a sleep interval
} WsbCatch(hr);
Unlock();
WsbTraceOut(OLESTR("CFsaTruncator::Pause"), OLESTR("hr = <%ls>"), WsbHrAsString(hr) );
return(hr);
}
HRESULT
CFsaTruncator::ProcessSessionEvent(
IN IHsmSession* pSession,
IN HSM_JOB_PHASE phase,
IN HSM_JOB_EVENT event
)
/*++
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::ProcessSessionEvent"), OLESTR(""));
try {
WsbAssert(0 != pSession, E_POINTER);
// If the phase applies to use (SCAN or ALL), then do any work required by the
// event.
if ((HSM_JOB_PHASE_ALL == phase) || (HSM_JOB_PHASE_SCAN == phase)) {
switch(event) {
case HSM_JOB_EVENT_SUSPEND:
case HSM_JOB_EVENT_CANCEL:
case HSM_JOB_EVENT_FAIL:
WsbAffirmHr(Cancel(event));
break;
case HSM_JOB_EVENT_PAUSE:
WsbAffirmHr(Pause());
break;
case HSM_JOB_EVENT_RESUME:
WsbAffirmHr(Resume());
break;
case HSM_JOB_EVENT_RAISE_PRIORITY:
WsbAffirmHr(RaisePriority());
break;
case HSM_JOB_EVENT_LOWER_PRIORITY:
WsbAffirmHr(LowerPriority());
break;
default:
case HSM_JOB_EVENT_START:
WsbAssert(FALSE, E_UNEXPECTED);
break;
}
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::ProcessSessionEvent"), OLESTR("hr = <%ls>"), WsbHrAsString(hr) );
return(S_OK);
}
HRESULT
CFsaTruncator::RaisePriority(
void
)
/*++
--*/
{
HRESULT hr = S_OK;
try {
WsbAssert(0 != m_threadHandle, E_UNEXPECTED);
WsbAssert(m_pSession != 0, E_UNEXPECTED);
switch(m_priority) {
case HSM_JOB_PRIORITY_IDLE:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_LOWEST));
m_priority = HSM_JOB_PRIORITY_LOWEST;
break;
case HSM_JOB_PRIORITY_LOWEST:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_BELOW_NORMAL));
m_priority = HSM_JOB_PRIORITY_LOW;
break;
case HSM_JOB_PRIORITY_LOW:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_NORMAL));
m_priority = HSM_JOB_PRIORITY_NORMAL;
break;
case HSM_JOB_PRIORITY_NORMAL:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_ABOVE_NORMAL));
m_priority = HSM_JOB_PRIORITY_HIGH;
break;
case HSM_JOB_PRIORITY_HIGH:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_HIGHEST));
m_priority = HSM_JOB_PRIORITY_HIGHEST;
break;
case HSM_JOB_PRIORITY_HIGHEST:
WsbAffirmStatus(SetThreadPriority(m_threadHandle, THREAD_PRIORITY_TIME_CRITICAL));
m_priority = HSM_JOB_PRIORITY_CRITICAL;
break;
default:
case HSM_JOB_PRIORITY_CRITICAL:
WsbAffirm(FALSE, E_UNEXPECTED);
break;
}
WsbAffirmHr(m_pSession->ProcessPriority(HSM_JOB_PHASE_SCAN, m_priority));
} WsbCatch(hr);
return(hr);
}
HRESULT
CFsaTruncator::Resume(
void
)
/*++
Implements:
IFsaTruncator::Resume().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::Resume"), OLESTR("state = %ls"),
FsaStateAsString(m_state));
// Lock this object to avoid having the state change between testing its value
// and setting it to a new value
Lock();
try {
// We should only see a resume from a paused state, so ignore the resume if we are
// in some other state. NOTE: This used to be an assert, but it scared people since it
// can occur occassionally.
if ((HSM_JOB_STATE_PAUSING == m_state) || (HSM_JOB_STATE_PAUSED == m_state)) {
WsbAffirmHr(SetState(HSM_JOB_STATE_ACTIVE));
}
} WsbCatch(hr);
Unlock();
WsbTraceOut(OLESTR("CFsaTruncator::Resume"), OLESTR("hr = <%ls>"), WsbHrAsString(hr) );
return(hr);
}
HRESULT
CFsaTruncator::Save(
IN IStream* pStream,
IN BOOL clearDirty
)
/*++
Implements:
IPersistStream::Save().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::Save"), OLESTR("clearDirty = <%ls>"), WsbBoolAsString(clearDirty));
try {
WsbAssert(0 != pStream, E_POINTER);
// Do the easy stuff, but make sure that this order matches the order
// in the save method.
WsbAffirmHr(WsbSaveToStream(pStream, static_cast<ULONG>(m_priority)));
WsbAffirmHr(WsbSaveToStream(pStream, m_maxFiles));
WsbAffirmHr(WsbSaveToStream(pStream, m_runInterval));
WsbAffirmHr(WsbSaveToStream(pStream, m_runId));
WsbAffirmHr(WsbSaveToStream(pStream, m_keepRecallTime));
WsbAffirmHr(WsbSaveToStream(pStream, static_cast<USHORT>(m_SortOrder)));
// 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("CFsaTruncator::Save"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::SetKeepRecallTime(
IN FILETIME time
)
/*++
Implements:
IFsaTruncator::SetKeepRecallTime().
--*/
{
WsbTraceIn(OLESTR("CFsaTruncator::SetKeepRecallTime"), OLESTR(""));
m_keepRecallTime = time;
WsbTraceOut(OLESTR("CFsaTruncator::SetKeepRecallTime"), OLESTR("hr = <%ls>"), WsbHrAsString(S_OK));
return(S_OK);
}
HRESULT
CFsaTruncator::SetMaxFilesPerRun(
IN LONGLONG maxFiles
)
/*++
Implements:
IFsaTruncator::SetMaxFilesPerRun().
--*/
{
WsbTraceIn(OLESTR("CFsaTruncator::SetMaxFilesPerRun"), OLESTR(""));
m_maxFiles = maxFiles;
WsbTraceOut(OLESTR("CFsaTruncator::SetMaxFilesPerRun"), OLESTR("hr = <%ls>"), WsbHrAsString(S_OK));
return(S_OK);
}
HRESULT
CFsaTruncator::SetPremigratedSortOrder(
IN FSA_PREMIGRATED_SORT_ORDER SortOrder
)
/*++
Implements:
IFsaTruncator::SetSortOrder().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::SetPremigratedSortOrder"), OLESTR("SortOrder = <%ls>"), FsaSortOrderAsString( SortOrder ) );
// This key has not been implmented yet.
if (FSA_SORT_PL_BY_SIZE_AND_TIME == SortOrder) {
hr = E_NOTIMPL;
} else {
m_SortOrder = SortOrder;
}
WsbTraceOut(OLESTR("CFsaTruncator::SetPremigratedSortOrder"), OLESTR("hr = <%ls> m_SortOrder = <%ls>"), WsbHrAsString(S_OK) , FsaSortOrderAsString( m_SortOrder ) );
return(hr);
}
HRESULT
CFsaTruncator::SetRunInterval(
IN ULONG milliseconds
)
/*++
Implements:
IFsaTruncator::SetRunInterval().
--*/
{
BOOL DoKick = FALSE;
WsbTraceIn(OLESTR("CFsaTruncator::SetRunInterval"), OLESTR("milliseconds = <%ls>"), WsbPtrToUlongAsString( &milliseconds ) );
if (milliseconds < m_runInterval) {
DoKick = TRUE;
}
m_runInterval = milliseconds;
// Wake up the Truncator if the interval has decreased
if (DoKick) {
KickStart();
}
WsbTraceOut(OLESTR("CFsaTruncator::SetRunInterval"), OLESTR("hr = <%ls> m_runInterval = <%ls>"), WsbHrAsString(S_OK), WsbPtrToUlongAsString( &m_runInterval ) );
return(S_OK);
}
HRESULT
CFsaTruncator::SetState(
IN HSM_JOB_STATE state
)
/*++
--*/
{
HRESULT hr = S_OK;
BOOL bLog = FALSE;
WsbTraceIn(OLESTR("CFsaTruncator::SetState"), OLESTR("state = <%ls>"), FsaStateAsString( state ) );
// Change the state and report the change to the session.
Lock();
m_state = state;
Unlock();
hr = m_pSession->ProcessState(HSM_JOB_PHASE_SCAN, m_state, m_currentPath, bLog);
WsbTraceOut(OLESTR("CFsaTruncator::SetState"), OLESTR("hr = <%ls> m_state = <%ls>"), WsbHrAsString(hr), FsaStateAsString( m_state ) );
return(hr);
}
HRESULT
CFsaTruncator::ChangeSysState(
IN OUT HSM_SYSTEM_STATE* pSysState
)
/*++
Implements:
IHsmSystemState::ChangeSysState().
--*/
{
HRESULT hr = S_OK;
WsbTraceIn(OLESTR("CFsaTruncator::ChangeSysState"), OLESTR("thread is %ls"),
(m_threadHandle ? OLESTR("active") : OLESTR("inactive")));
try {
if (pSysState->State & HSM_STATE_SUSPEND) {
if (HSM_JOB_STATE_ACTIVE == m_state) {
Pause();
}
} else if (pSysState->State & HSM_STATE_RESUME) {
if ((HSM_JOB_STATE_PAUSING == m_state) ||
(HSM_JOB_STATE_PAUSED == m_state)) {
Resume();
}
} else if (pSysState->State & HSM_STATE_SHUTDOWN) {
// Make sure the thread is stopped
if (m_threadHandle) {
m_state = HSM_JOB_STATE_DONE;
if (m_event) {
SetEvent(m_event);
}
// Wait for the thread to end
if (m_threadHandle) {
WsbTrace(OLESTR("CFsaTruncator::ChangeSysState, waiting for truncator thread to end\n"));
switch (WaitForSingleObject(m_threadHandle, 120000)) {
case WAIT_FAILED:
WsbTrace(OLESTR("CFsaTruncator::ChangeSysState, WaitforSingleObject returned error %lu\n"),
GetLastError());
break;
case WAIT_TIMEOUT:
WsbTrace(OLESTR("CFsaTruncator::ChangeSysState, timeout.\n"));
break;
default:
break;
}
}
// If the thread is still active, terminate it
if (m_threadHandle) {
WsbTrace(OLESTR("CFsaTruncator::ChangeSysState: calling TerminateThread\n"));
if (!TerminateThread(m_threadHandle, 0)) {
WsbTrace(OLESTR("CFsaTruncator::ChangeSysState: TerminateThread returned error %lu\n"),
GetLastError());
}
}
}
if (m_event) {
CloseHandle(m_event);
m_event = 0;
}
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::ChangeSysState"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
return(hr);
}
HRESULT
CFsaTruncator::Start(
IFsaResource* pResource
)
/*++
Implements:
IFsaTruncator::Start().
--*/
{
HRESULT hr = S_OK;
CComPtr<IHsmSession> pSession;
CComPtr<IConnectionPointContainer> pCPC;
CComPtr<IConnectionPoint> pCP;
CComPtr<IHsmSessionSinkEveryEvent> pSink;
CWsbStringPtr name;
WsbTraceIn(OLESTR("CFsaTruncator::Start"), OLESTR("m_state = <%ls>"), FsaStateAsString( m_state ) );
try {
if (0 == m_threadId) {
//
// If the thread is dead, start one.
//
// Make sure that we don't already have a session, and that we haven't started already.
WsbAssert(m_pSession == 0, E_UNEXPECTED);
WsbAssert( (HSM_JOB_STATE_IDLE == m_state) || (HSM_JOB_STATE_DONE == m_state) ||
(HSM_JOB_STATE_CANCELLED == m_state) || (HSM_JOB_STATE_FAILED == m_state), E_UNEXPECTED);
// Get the name for the session, increment the runId, and reset the subRunId.
WsbAffirmHr(name.LoadFromRsc(_Module.m_hInst, IDS_FSA_TRUNCATOR_NAME));
m_runId++;
m_subRunId = 0;
// Begin a Session.
WsbAffirmHr(pResource->BeginSession(name, HSM_JOB_LOG_NONE, m_runId, m_subRunId, &pSession));
m_pSession = pSession;
// Ask the session to advise of every event.
WsbAffirmHr(pSession->QueryInterface(IID_IConnectionPointContainer, (void**) &pCPC));
WsbAffirmHr(pCPC->FindConnectionPoint(IID_IHsmSessionSinkEveryEvent, &pCP));
WsbAffirmHr(((IUnknown*) (IFsaTruncator*) this)->QueryInterface(IID_IHsmSessionSinkEveryEvent, (void**) &pSink));
WsbAffirmHr(pCP->Advise(pSink, &m_cookie));
try {
if (0 == m_event) {
WsbAssertHandle(m_event = CreateEvent(NULL, FALSE, FALSE, NULL));
}
// Now that we have prepared, create the thread that will do the scanning!
WsbAffirm((m_threadHandle = CreateThread(0, 0, FsaStartTruncator, (void*) this, 0, &m_threadId)) != 0, HRESULT_FROM_WIN32(GetLastError()));
} WsbCatchAndDo(hr, SetState(HSM_JOB_STATE_FAILED););
} else {
// The thread is still alive, just keep it going. If it is in a state that would
// cause it to exit, then make it active again.
WsbAssert(m_pSession != 0, E_UNEXPECTED);
if ((HSM_JOB_STATE_ACTIVE != m_state) && (HSM_JOB_STATE_PAUSING != m_state) &&
(HSM_JOB_STATE_PAUSED != m_state) && (HSM_JOB_STATE_RESUMING != m_state)) {
WsbAffirmHr(SetState(HSM_JOB_STATE_ACTIVE));
}
}
} WsbCatch(hr);
WsbTraceOut(OLESTR("CFsaTruncator::Start"), OLESTR("hr = <%ls> m_state = <%ls>"), WsbHrAsString(hr), FsaStateAsString( m_state ) );
return(hr);
}
HRESULT
CFsaTruncator::StartScan(
void
)
/*++
--*/
{
ULONG adjustedFreeLevel = 0;
HRESULT hr = S_OK;
HRESULT hr2;
LONGLONG itemOffset;
LONGLONG itemSize = 0;
LONGLONG fileId;
ULONG freeLevel;
ULONG hsmLevel;
BOOL skipFile;
BOOL dummy;
LONGLONG llLastTruncTime = 0;
LONGLONG llRunIntervalTicks;
FILETIME recallTime, currentTime, accessTime, criteriaTime, premRecAccessTime;
LONGLONG totalVolumeSpace;
CComPtr<IFsaResource> pResource;
CComPtr<IFsaResourcePriv> pResourcePriv;
CComPtr<IFsaScanItem> pScanItem;
CComPtr<IFsaPremigratedRec> pPremRec;
CComPtr<IConnectionPointContainer> pCPC;
CComPtr<IConnectionPoint> pCP;
try {
WsbTrace(OLESTR("CFsaTruncator::StartScan - starting loop\n"));
// Increment the ref count so this object (or its session) doesn't
// get released before this thread ends
((IUnknown *)(IFsaTruncator *)this)->AddRef();
WsbAssert(m_pSession != 0, E_POINTER);
// The thread is running.
WsbAffirmHr(SetState(HSM_JOB_STATE_ACTIVE));
// Get the resource
WsbAffirmHr(m_pSession->GetResource(&pResource));
WsbAffirmHr(pResource->QueryInterface(IID_IFsaResourcePriv, (void**) &pResourcePriv));
WsbAffirmHr(pResource->GetSizes(&totalVolumeSpace, NULL, NULL, NULL));
// Start with the first path.
while ((HSM_JOB_STATE_ACTIVE == m_state) || (HSM_JOB_STATE_PAUSING == m_state) ||
(HSM_JOB_STATE_PAUSED == m_state) || (HSM_JOB_STATE_RESUMING == m_state)) {
WsbTrace(OLESTR("CFsaTruncator::StartScan, top of outside while loop, state = <%ls>\n"),
FsaStateAsString( m_state ) );
// If the truncator is running and the resource does not have enough free space
// check to see if the resource is over the threshold and truncation is needed.
WsbAffirmHr(pResource->GetHsmLevel(&hsmLevel));
WsbAffirmHr(pResource->GetFreeLevel(&freeLevel));
// Because the truncation is asynchronous (FsaPostIt is sent to Engine for
// verification and then returned to FSA for actual truncation), the
// measured freeLevel may not be very accurate if there are truncations
// pending. To compensate for this, we keep an adjustedFreeLevel which
// attempts to take into account the pending truncates. We synchronize the
// adjustedFreeLevel to the measured freeLevel the first time through and
// after we have slept for a while (on the assumption that the pending
// truncates have had time to be performed). This still leaves open the possiblility
// that the measured freeLevel is wrong (because truncates are pending), but
// should be an improvement over just using the measured freeLevel.
llRunIntervalTicks = m_runInterval * (WSB_FT_TICKS_PER_SECOND / 1000);
GetSystemTimeAsFileTime(&currentTime);
if (0 == adjustedFreeLevel ||
((WsbFTtoLL(currentTime) - llLastTruncTime) > llRunIntervalTicks)) {
adjustedFreeLevel = freeLevel;
WsbTrace(OLESTR("CFsaTruncator::StartScan, resetting adjusted free level, RunInterval = %ls, time diff = %ls\n"),
WsbQuickString(WsbLonglongAsString(llRunIntervalTicks)),
WsbQuickString(WsbLonglongAsString(WsbFTtoLL(currentTime) - llLastTruncTime)));
}
WsbTrace(OLESTR("CFsaTruncator::StartScan, desired level = %u, free level = %u, adjusted free level = %u\n"),
hsmLevel, freeLevel, adjustedFreeLevel);
if (adjustedFreeLevel < hsmLevel && HSM_JOB_STATE_ACTIVE == m_state) {
CComPtr<IWsbDbSession> pDbSession;
CComPtr<IFsaPremigratedDb> pPremDb;
// Open the premigration list, and set the order in which it will be scanned.
WsbAffirmHr(pResourcePriv->GetPremigrated(IID_IFsaPremigratedDb,
(void**) &pPremDb));
WsbAffirmHr(pPremDb->Open(&pDbSession));
try {
WsbAffirmHr(pPremDb->GetEntity(pDbSession, PREMIGRATED_REC_TYPE, IID_IFsaPremigratedRec, (void**) &pPremRec));
// Set the order to get items from the Premigrated List
switch (m_SortOrder) {
case FSA_SORT_PL_BY_SIZE:
WsbAffirmHr(pPremRec->UseKey(PREMIGRATED_SIZE_KEY_TYPE));
break;
case FSA_SORT_PL_BY_PATH_NAME:
// We use the BagId and offsets instead
WsbAffirmHr(pPremRec->UseKey(PREMIGRATED_BAGID_OFFSETS_KEY_TYPE));
break;
case FSA_SORT_PL_BY_SIZE_AND_TIME:
// We don't know how to handle this one yet
WsbThrow(E_UNEXPECTED);
break;
case FSA_SORT_PL_BY_ACCESS_TIME:
default:
WsbAffirmHr(pPremRec->UseKey(PREMIGRATED_ACCESS_TIME_KEY_TYPE));
break;
}
// Make a pass through the list of premigrated files until the
// desired level has been reached. Some items that are on the
// list may be in a state that causes them to be skipped, but left on the list.
WsbAffirmHr(pPremRec->First());
while ((adjustedFreeLevel < hsmLevel) && (HSM_JOB_STATE_ACTIVE == m_state)) {
CComPtr<IFsaRecoveryRec> pRecRec;
WsbTrace(OLESTR("CFsaTruncator::StartScan (top of inside while loop) desired level = %u, adjusted free level = %u\n"),
hsmLevel, adjustedFreeLevel);
try {
skipFile = FALSE;
//
// Get the access time as recorded in the premigrated record
// Note that the real access time cannot be older than the one
// in the premigrated list but it can be newer
//
WsbAffirmHr(pPremRec->GetAccessTime(&premRecAccessTime));
WsbAffirmHr(pResource->GetManageableItemAccessTime(&dummy, &criteriaTime));
if (WsbCompareFileTimes(premRecAccessTime, criteriaTime, TRUE, FALSE) < 0 ) {
if (pPremRec->IsWaitingForClose() == S_FALSE) {
//
// Can skip the current file but NOT break out of the loop since
// files with access time old enough and WaitingForClose flag set
// may still exists in the list
//
skipFile = TRUE;
} else {
//
// The access time in the prem. rec is within the window.
// This means there aren't any other records which are outside the
// user-desired last access window. So break out
//
WsbTrace(OLESTR("CFsaTruncator::StartScan: breaking out of auto-truncator, encountered item with access time not within criteria\n"));
hr = WSB_E_NOTFOUND;
break;
}
}
// Get information about the file that could be truncated.
WsbAffirmHr(pPremRec->GetFileId(&fileId));
WsbAffirmHr(pPremRec->GetOffset(&itemOffset));
WsbAffirmHr(pPremRec->GetSize(&itemSize));
m_currentPath.Free();
WsbAffirmHr(pPremRec->GetPath(&m_currentPath, 0));
WsbAffirmHr(pPremRec->GetRecallTime(&recallTime));
GetSystemTimeAsFileTime(&currentTime);
// Make sure that this file wasn't recently recalled. For now,
// this will check for 1 minute.
if ((! skipFile) &&
( (pPremRec->IsWaitingForClose() == S_FALSE) ||
((WsbFTtoLL(currentTime) > WsbFTtoLL(recallTime)) &&
(WsbCompareFileTimes(recallTime, m_keepRecallTime, TRUE, FALSE) >= 0)) )) {
hr = pResource->FindFileId(fileId, m_pSession, &pScanItem);
if (hr == WSB_E_NOTFOUND) {
//
// The file does not exist anymore - remove the record from the list.
//
WsbAffirmHr(pDbSession->TransactionBegin());
try {
// Make sure the record is still in the DB
WsbAffirmHr(pPremRec->FindEQ());
WsbAffirmHr(pPremRec->Remove());
WsbAffirmHr(pResourcePriv->RemovePremigratedSize(itemSize));
} WsbCatch(hr);
WsbAffirmHr(pDbSession->TransactionEnd());
WsbThrow(hr);
} else if (hr != S_OK) {
//
// Any other error is unexpected - log it and continue
//
WsbLogEvent(FSA_E_ACCESS_ERROR, 0, NULL, m_currentPath, WsbHrAsString(hr), NULL);
WsbThrow(hr);
}
//
// Verify that the file is still in a premigrated state
//
if (S_OK == pScanItem->IsPremigrated(itemOffset, itemSize)) {
WsbAffirmHr(pScanItem->GetAccessTime(&accessTime));
//
// accessTime is the last access time for the file
// criteriaTime is the 'not accessed in so many ticks' criteria for truncating
// the file.
// So if (currentTime - accessTime) >= criteriaTime, then the file is ok to be truncated
//
if (WsbCompareFileTimes(accessTime, criteriaTime, TRUE, FALSE) >=0 ) {
//
// The file was not accessed within the last access window
//
WsbTrace(OLESTR("CFsaTruncator::StartScan, truncating file <%ls>\n"),
(WCHAR *)m_currentPath);
// Try to truncate the file.
try {
// Create and save a recovery record in case something goes wrong
WsbAffirmHr(pPremDb->GetEntity(pDbSession, RECOVERY_REC_TYPE, IID_IFsaRecoveryRec, (void**) &pRecRec));
WsbAffirmHr(pRecRec->SetPath(m_currentPath));
// If the record already exists rewrite it, otherwise create a new record.
hr2 = pRecRec->FindEQ();
if (WSB_E_NOTFOUND == hr2) {
hr2 = S_OK;
WsbAffirmHr(pRecRec->MarkAsNew());
} else if (FAILED(hr2)) {
WsbThrow(hr2);
}
WsbAffirmHr(pRecRec->SetFileId(fileId));
WsbAffirmHr(pRecRec->SetOffsetSize(itemOffset, itemSize));
WsbAffirmHr(pRecRec->SetStatus(FSA_RECOVERY_FLAG_TRUNCATING));
WsbAffirmHr(pRecRec->Write());
//
// Set the waiting for close flag to prevent this file
// from being selected again while the engine is
// processing the truncate. Set the recall time to
// now plus 1 hour so we are sure not to retry this
// until we have had a chance to truncate it.
//
WsbAffirmHr(pPremRec->SetIsWaitingForClose(TRUE));
WsbAffirmHr(pPremRec->SetRecallTime(WsbLLtoFT(WsbFTtoLL(currentTime) + WSB_FT_TICKS_PER_HOUR)));
hr2 = pPremRec->Write();
// Special code to deal with a problem that has been seen
// but isn't understood
if (WSB_E_IDB_PRIMARY_KEY_CHANGED == hr2) {
WsbAffirmHr(pPremRec->Remove());
WsbAffirmHr(pResourcePriv->RemovePremigratedSize(itemSize));
// Ignore result from DeletePlaceholder since there's nothing we
// can do anyway.
pScanItem->DeletePlaceholder(itemOffset, itemSize);
WsbThrow(FSA_E_SKIPPED);
} else {
WsbAffirmHr(hr2);
}
//
// Set IsWaitingForClose back to false so that the FindGt done later gets the next record.
// This affects the in memory record only and not the persisted record.
//
WsbAffirmHr(pPremRec->SetIsWaitingForClose(FALSE));
WsbAffirmHr(pScanItem->Truncate(itemOffset, itemSize));
llLastTruncTime = WsbFTtoLL(currentTime);
// Add the file size to the adjustedFreeLevel so we know when to
// stop doing truncations. Unfortunately, the itemSize is in
// bytes but adjustedFreeLevl is a fixed-point percentage so we
// have to do a calculation to convert the itemSize
adjustedFreeLevel += (ULONG) (((double)itemSize /
(double)totalVolumeSpace) *
(double)FSA_HSMLEVEL_100);
} WsbCatchAndDo(hr,
// Do we need to skip this file for the time being?
if (FSA_E_SKIPPED == hr) {
// Do nothing
} else if ((FSA_E_ITEMCHANGED != hr) && (FSA_E_NOTMANAGED != hr)) {
// Something unexpected happened, so report the error.
WsbAffirmHr(m_pSession->ProcessHr(HSM_JOB_PHASE_FSA_ACTION, 0, 0, hr));
}
);
} else {
//
// File is premigrated, but skipped because the last access was too recent
//
WsbTrace(OLESTR("CFsaTruncator::StartScan, skipping file <%ls> which is premigrated but last access is too recent\n"),
(WCHAR *)m_currentPath);
hr = FSA_E_SKIPPED;
//
// Update the access time in the db for this file
//
WsbAffirmHr(pPremRec->SetAccessTime(accessTime));
//
// Commit this
//
WsbAffirmHr(pPremRec->Write());
//
// Revert the in-memory accessTime to the old access time to
// let the enumeration continue (so that FindGT will fetch the next record)
//
WsbAffirmHr(pPremRec->SetAccessTime(premRecAccessTime));
}
} else {
//
// If the file is no longer managed by HSM or truncated (may have been modified
// after it was premigrated) - we remove the record from the list.
// Note that if we reached this else close, the condition below should be TRUE
//
if ( (S_FALSE == pScanItem->IsManaged(itemOffset, itemSize)) ||
(S_OK == pScanItem->IsTruncated(itemOffset, itemSize)) ) {
WsbAffirmHr(pDbSession->TransactionBegin());
try {
// Make sure the record is still in the DB
WsbAffirmHr(pPremRec->FindEQ());
WsbAffirmHr(pPremRec->Remove());
WsbAffirmHr(pResourcePriv->RemovePremigratedSize(itemSize));
} WsbCatch(hr);
WsbAffirmHr(pDbSession->TransactionEnd());
// Ignore hr of the removal itself (truncated files may have been removed by another thread)
hr = WSB_E_NOTFOUND;
WsbThrow(hr);
}
}
// Tell the session we saw the file, and whether we were able to truncate it.
WsbAffirmHr(m_pSession->ProcessItem(HSM_JOB_PHASE_FSA_ACTION, HSM_JOB_ACTION_TRUNCATE, pScanItem, hr));
// Don't let this errors stop us from continuing to process the list.
hr = S_OK;
} else {
//
// File is premigrated, but skipped because the last access was too recent or
// because it was recalled recently
//
WsbTrace(OLESTR("CFsaTruncator::StartScan, skipping file <%ls> since its last access time is too recent or recently recalled\n"),
(WCHAR *)m_currentPath);
hr = FSA_E_SKIPPED;
}
} WsbCatchAndDo(hr,
if (WSB_E_NOTFOUND != hr) {
m_pSession->ProcessHr(HSM_JOB_PHASE_FSA_ACTION, __FILE__, __LINE__, hr);
}
// Don't let this errors stop us from continuing to process the list.
hr = S_OK;
);
// If item is skipped - set hr to OK (this is not really an error)
if (FSA_E_SKIPPED == hr) {
hr = S_OK;
}
// Remove recovery record
if (pRecRec) {
WsbAffirmHr(pRecRec->FindEQ());
WsbAffirmHr(pRecRec->Remove());
pRecRec = NULL;
}
// Get the desired level again in case it changed
WsbAffirmHr(pResource->GetHsmLevel(&hsmLevel));
// Free the scan item.
pScanItem = 0;
// Whether we removed or skipped the item, go on to the next item.
WsbAffirmHr(pPremRec->FindGT());
WsbTrace(OLESTR("CFsaTruncator::StartScan, bottom of inside while loop, state = <%ls>\n"),
FsaStateAsString( m_state ) );
} // inner while
} WsbCatch(hr);
// Free the premigrated record object and close the data base.
try {
pPremRec = 0;
WsbAffirmHr(pPremDb->Close(pDbSession));
} WsbCatchAndDo(hr2,
m_pSession->ProcessHr(HSM_JOB_PHASE_ALL, __FILE__, __LINE__, hr2);
);
}
// Sleep or wait for an event signal.
// If the event is signaled it means that data was just moved for this
// volume and there should be something to do.
if (SUCCEEDED(hr) || WSB_E_NOTFOUND == hr) {
ULONG l_runInterval;
// If we got to the end of the list, then wait a little longer. This
// is because we probably won't be able to do anything when we retry.
if (WSB_E_NOTFOUND == hr) {
l_runInterval = m_runInterval * 10;
} else {
l_runInterval = m_runInterval;
}
WsbTrace(OLESTR("CFsaTruncator::StartScan, sleeping for %lu msec\n"), l_runInterval);
switch(WaitForSingleObject(m_event, l_runInterval)) {
case WAIT_FAILED:
WsbTrace(OLESTR("CFsaTruncator::StartScan, Wait for Single Object returned error %lu\n"),
GetLastError());
break;
case WAIT_TIMEOUT:
WsbTrace(OLESTR("CFsaTruncator::StartScan, Awakened by timeout.\n"));
// Set adjustedFreeLevel to zero so it will get reset to current freeLevel;
adjustedFreeLevel = 0;
break;
default:
WsbTrace(OLESTR("CFsaTruncator::StartScan, Awakened by kick start.\n"));
break;
}
} else {
WsbThrow(hr);
}
WsbTrace(OLESTR("CFsaTruncator::StartScan, bottom of outside while loop, state = <%ls>\n"),
FsaStateAsString( m_state ) );
}
} WsbCatch(hr);
m_threadHr = hr;
// The thread is exiting, so tell the session.
if (FAILED(hr)) {
hr2 = SetState(HSM_JOB_STATE_FAILED);
} else {
hr2 = SetState(HSM_JOB_STATE_DONE);
}
if (FAILED(hr2)) {
m_pSession->ProcessHr(HSM_JOB_PHASE_ALL, __FILE__, __LINE__, hr2);
}
// Regardless of how this thread is exiting, we need to unadvise from the session.
// Indicate that we no longer want to be advised of events.
if ((m_pSession != 0) && (m_cookie != 0)) {
try {
WsbAffirmHr(m_pSession->QueryInterface(IID_IConnectionPointContainer, (void**) &pCPC));
WsbAffirmHr(pCPC->FindConnectionPoint(IID_IHsmSessionSinkEveryEvent, &pCP));
pCP->Unadvise(m_cookie);
m_cookie = 0;
} WsbCatch(hr);
}
// Since we have terminated, we should release the session.
m_pSession = 0;
// Clean up after this thread.
CloseHandle(m_threadHandle);
m_threadId = 0;
m_threadHandle = 0;
// Decrement ref count so this object can be release
((IUnknown *)(IFsaTruncator *)this)->Release();
WsbTrace(OLESTR("CFsaTruncator::StartScan - terminating, hr = <%ls>, m_state = <%ls>\n"),
WsbHrAsString(hr), FsaStateAsString( m_state ) );
return(hr);
}