/*++

© 1998 Seagate Software, Inc.  All rights reserved.

Module Name:

    fsaitem.cpp

Abstract:

    This class contains represents a scan item (i.e. file or directory) for NTFS 5.0.

Author:

    Chuck Bardeen    [cbardeen]   1-Dec-1996

Revision History:

--*/

#include "stdafx.h"

#define WSB_TRACE_IS        WSB_TRACE_BIT_FSA

#include "wsb.h"
#include "wsbtrak.h"
#include "fsa.h"
#include "mover.h"
#include "fsaitem.h"
#include "fsaprem.h"

static USHORT iCountItem = 0;  // Count of existing objects



HRESULT
CFsaScanItem::CompareTo(
    IN IUnknown* pUnknown,
    OUT SHORT* pResult
    )

/*++

Implements:

  IWsbCollectable::CompareTo().

--*/
{
    HRESULT                  hr = S_OK;
    CComPtr<IFsaScanItem>    pScanItem;

    WsbTraceIn(OLESTR("CFsaScanItem::CompareTo"), OLESTR(""));

    try {

        // Did they give us a valid item to compare to?
        WsbAssert(0 != pUnknown, E_POINTER);

        // We need the IWsbBool interface to get the value of the object.
        WsbAffirmHr(pUnknown->QueryInterface(IID_IFsaScanItem, (void**) &pScanItem));

        // Compare the rules.
        hr = CompareToIScanItem(pScanItem, pResult);

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::CompareTo"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult));

    return(hr);
}


HRESULT
CFsaScanItem::CompareToIScanItem(
    IN IFsaScanItem* pScanItem,
    OUT SHORT* pResult
    )

/*++

Implements:

  IFsaScanItem::CompareToIScanItem().

--*/
{
    HRESULT          hr = S_OK;
    CWsbStringPtr    path;
    CWsbStringPtr    name;

    WsbTraceIn(OLESTR("CFsaScanItem::CompareToIScanItem"), OLESTR(""));

    try {

        // Did they give us a valid item to compare to?
        WsbAssert(0 != pScanItem, E_POINTER);

        // Either compare the name or the id.
           WsbAffirmHr(pScanItem->GetPath(&path, 0));
           WsbAffirmHr(pScanItem->GetName(&name, 0));
           hr = CompareToPathAndName(path, name, pResult);

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::CompareToIScanItem"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult));

    return(hr);
}


HRESULT
CFsaScanItem::CompareToPathAndName(
    IN OLECHAR* path,
    IN OLECHAR* name,
    OUT SHORT* pResult
    )

/*++

Implements:

  IFsaScanItem::CompareToPathAndName().

--*/
{
    HRESULT       hr = S_OK;
    SHORT         aResult = 0;

    WsbTraceIn(OLESTR("CFsaScanItem::CompareToPathAndName"), OLESTR(""));

    try {

        // Compare the path.
        aResult = (SHORT) _wcsicmp(m_path, path);

        // Compare the name.
        if (0 == aResult) {
            aResult = (SHORT) _wcsicmp(m_findData.cFileName, name);
        }

        if (0 != aResult) {
            hr = S_FALSE;
        }

        if (0 != pResult) {
            *pResult = aResult;
        }

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::CompareToPathAndName"), OLESTR("hr = <%ls>, result = <%u>"), WsbHrAsString(hr), aResult);

    return(hr);
}


HRESULT
CFsaScanItem::Copy(
    IN OLECHAR* dest,
    IN BOOL /*retainHierarcy*/,
    IN BOOL /*expandPlaceholders*/,
    IN BOOL overwriteExisting
    )

/*++

Implements:

  IFsaScanItem::Copy().

--*/
{
    HRESULT            hr = S_OK;

    try {

        // NOTE : This default behavior causes placeholders
        // to be expanded and probably doesn't retain the heirarchy.
        WsbAssert(0 != dest, E_POINTER);
        WsbAssert(CopyFile(m_findData.cFileName, dest, overwriteExisting), E_FAIL);

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::CreateLocalStream(
    OUT IStream **ppStream
    )

/*++

Implements:

  IFsaScanItem::CreateLocalStream().

--*/
{
    HRESULT          hr = S_OK;
    LARGE_INTEGER    fileSize;
    CWsbStringPtr    volName;

    WsbTraceIn(OLESTR("CFsaScanItem::CreateLocalStream"), OLESTR(""));
    try {
        CWsbStringPtr    localName;

        if ( !m_gotPlaceholder) {
            //
            // Get the placeholder info
            //
            fileSize.LowPart = m_findData.nFileSizeLow;
            fileSize.HighPart = m_findData.nFileSizeHigh;
            WsbAffirmHr(IsManaged(0, fileSize.QuadPart));
        }

        WsbAssert( 0 != ppStream, E_POINTER);
        WsbAffirmHr( CoCreateInstance( CLSID_CNtFileIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)&m_pDataMover ) );
        //
        // Set the device name for the mover so it can set the source infor for the USN journal.
        //
        WsbAffirmHr(m_pResource->GetPath(&volName, 0));
        WsbAffirmHr( m_pDataMover->SetDeviceName(volName));
        //WsbAffirmHr(GetFullPathAndName( NULL, 0, &localName, 0));
        WsbAffirmHr(GetFullPathAndName( OLESTR("\\\\?\\"), 0, &localName, 0));
        WsbAffirmHr( m_pDataMover->CreateLocalStream(
                localName, MVR_MODE_WRITE | MVR_FLAG_HSM_SEMANTICS | MVR_FLAG_POSIX_SEMANTICS, &m_pStream ) );

        LARGE_INTEGER seekTo;
        ULARGE_INTEGER pos;
        seekTo.QuadPart = m_placeholder.dataStreamStart;
        WsbAffirmHr( m_pStream->Seek( seekTo, STREAM_SEEK_SET, &pos ) );
        *ppStream = m_pStream;
        m_pStream->AddRef();

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::CreateLocalStream"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}

HRESULT
CFsaScanItem::Delete(
    void
    )

/*++

Implements:

  IFsaScanItem::Delete().

--*/
{
    HRESULT             hr = S_OK;
    CWsbStringPtr       tmpString;
    HANDLE              fileHandle;

    try {

        // This is the name of the file we want to delete.
        WsbAffirmHr(GetFullPathAndName(OLESTR("\\\\?\\"), 0, &tmpString, 0));

        // Since we want to be POSIX compliant, we can't use DeleteFile() and instead will
        // open with the delete on close flag. This doesn't handle read-only files, so we
        // have to change that ourselves.
        WsbAffirmHr(MakeReadWrite());

        fileHandle = CreateFile(tmpString, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);

        if (INVALID_HANDLE_VALUE == fileHandle) {
            WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
        } else {
            if (!CloseHandle(fileHandle)) {
                WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
            }
        }

    } WsbCatch(hr);

    return(hr);
}
#pragma optimize("g", off)

HRESULT
CFsaScanItem::FinalConstruct(
    void
    )

/*++

Implements:

  CComObjectRoot::FinalConstruct().

--*/
{
    HRESULT        hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::FinalConstruct"), OLESTR(""));

    try {

        WsbAffirmHr(CComObjectRoot::FinalConstruct());

        m_handle = INVALID_HANDLE_VALUE;
        m_gotPhysicalSize = FALSE;
        m_physicalSize.QuadPart = 0;
        m_gotPlaceholder  = FALSE;
        m_changedAttributes = FALSE;
        m_handleRPI = 0;

        //  Add class to object table
        WSB_OBJECT_ADD(CLSID_CFsaScanItemNTFS, this);

    } WsbCatch(hr);

    iCountItem++;

    WsbTraceOut(OLESTR("CFsaScanItem::FinalConstruct"), OLESTR("hr = <%ls>, Count is <%d>"),
            WsbHrAsString(hr), iCountItem);

    return(hr);
}
#pragma optimize("", on)


void
CFsaScanItem::FinalRelease(
    void
    )

/*++

Implements:

  CComObjectRoot::FinalRelease().

--*/
{
    WsbTraceIn(OLESTR("CFsaScanItem::FinalRelease"), OLESTR(""));

    //  Subtract class from object table
    WSB_OBJECT_SUB(CLSID_CFsaScanItemNTFS, this);

    // Terminate the scan and free the path memory.
    if (INVALID_HANDLE_VALUE != m_handle) {
        FindClose(m_handle);
        m_handle = INVALID_HANDLE_VALUE;
    }
    if (0 != m_handleRPI) {
        CloseHandle(m_handleRPI);
        m_handleRPI = 0;
    }

    if (m_pUnmanageDb != NULL) {
        // Db must be open
        (void)m_pUnmanageDb->Close(m_pDbSession);
        m_pDbSession = 0;
        m_pUnmanageRec = 0;
    }

    if (TRUE == m_changedAttributes) {
        //
        // We changed it from read only to read/write - put it back.
        //
        RestoreAttributes();
    }

    //
    // Detach the data mover stream
    if (m_pDataMover != 0) {
        WsbAffirmHr( m_pDataMover->CloseStream() );
    }

    // Let the parent class do his thing.
    CComObjectRoot::FinalRelease();

    iCountItem--;
    WsbTraceOut(OLESTR("CFsaScanItem::FinalRelease"), OLESTR("Count is <%d>"), iCountItem);
}


HRESULT
CFsaScanItem::FindFirst(
    IN IFsaResource* pResource,
    IN OLECHAR* path,
    IN IHsmSession* pSession
    )

/*++

Implements:

  IFsaScanItem::FindFirst().

--*/
{
    HRESULT                  hr = S_OK;
    CWsbStringPtr            findPath;
    CWsbStringPtr            searchName;
    OLECHAR*                 slashPtr;
    DWORD                    lErr;

    WsbTraceIn(OLESTR("CFsaScanItem::FindFirst"), OLESTR("path = <%ls>"),
            path);

    try {

        WsbAssert(0 != pResource, E_POINTER);
        WsbAssert(0 != path, E_POINTER);

        // Store off some of the scan information.
        m_pResource = pResource;
        m_pSession = pSession;

        // Break up the incoming path into a path and a name.
        m_path = path;
        slashPtr = wcsrchr(m_path, L'\\');

        // We could try to support relative path stuff (i.e. current
        // directory, but I am not going to do it for now.
        WsbAffirm(slashPtr != 0, E_FAIL);
        searchName = &(slashPtr[1]);
        slashPtr[1] = 0;

        // Get a path that can be used by the find function.
        WsbAffirmHr(GetPathForFind(searchName, &findPath, 0));

        // Scan starting at the specified path.
        m_handle = FindFirstFileEx(findPath, FindExInfoStandard, &m_findData, FindExSearchNameMatch, 0, FIND_FIRST_EX_CASE_SENSITIVE);

        lErr = GetLastError();

        // If we found a file, then remember the scan handle and
        // return the scan item.
        WsbAffirm(INVALID_HANDLE_VALUE != m_handle, WSB_E_NOTFOUND);

        m_gotPhysicalSize = FALSE;
        m_physicalSize.QuadPart = 0;
        m_gotPlaceholder  = FALSE;

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::FindFirst"), OLESTR("hr = <%ls>"),
            WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaScanItem::FindNext(
    void
    )

/*++

Implements:

  IFsaScanItem::FindNext().

--*/
{
    HRESULT                    hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::FindNext"), OLESTR(""));

    try {

        WsbAssert(INVALID_HANDLE_VALUE != m_handle, E_FAIL);

        if (TRUE == m_changedAttributes) {
            //
            // We changed it from read only to read/write - put it back.
            //
            RestoreAttributes();
        }

        // Continue the scan.
        WsbAffirm(FindNextFile(m_handle, &m_findData), WSB_E_NOTFOUND);

        m_gotPhysicalSize = FALSE;
        m_physicalSize.QuadPart = 0;
        m_gotPlaceholder  = FALSE;

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::FindNext"), OLESTR("hr = <%ls>"),
            WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaScanItem::GetAccessTime(
    OUT FILETIME* pTime
    )

/*++

Implements:

  IFsaScanItem::GetAccessTime().

--*/
{
    HRESULT            hr = S_OK;

    try {

        WsbAssert(0 != pTime, E_POINTER);
        *pTime = m_findData.ftLastAccessTime;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetGroup(
    OUT OLECHAR** /*pGroup*/,
    IN ULONG /*bufferSize*/
    )

/*++

Implements:

  IFsaScanItem::GetGroup().

--*/
{
    HRESULT            hr = S_OK;

    try {

        hr = E_NOTIMPL;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetLogicalSize(
    OUT LONGLONG* pSize
    )

/*++

Implements:

  IFsaScanItem::GetLogicalSize().

--*/
{
    HRESULT            hr = S_OK;
    LARGE_INTEGER   logSize;

    try {

        WsbAssert(0 != pSize, E_POINTER);
        logSize.LowPart = m_findData.nFileSizeLow;
        logSize.HighPart = m_findData.nFileSizeHigh;
        *pSize = logSize.QuadPart;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetModifyTime(
    OUT FILETIME* pTime
    )

/*++

Implements:

  IFsaScanItem::GetModifyTime().

--*/
{
    HRESULT            hr = S_OK;

    try {

        WsbAssert(0 != pTime, E_POINTER);
        *pTime = m_findData.ftLastWriteTime;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetName(
    OUT OLECHAR** pName,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaScanItem::GetName().

--*/
{
    HRESULT            hr = S_OK;
    CWsbStringPtr    tmpString = m_findData.cFileName;

    try {

        WsbAssert(0 != pName, E_POINTER);
        WsbAffirmHr(tmpString.CopyTo(pName, bufferSize));

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetOwner(
    OUT OLECHAR** /*pOwner*/,
    IN ULONG      /*bufferSize*/
    )

/*++

Implements:

  IFsaScanItem::GetOwner().

--*/
{
    HRESULT            hr = S_OK;

    try {

        hr = E_NOTIMPL;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetPath(
    OUT OLECHAR** pPath,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaScanItem::GetPath().

--*/
{
    HRESULT            hr = S_OK;

    try {

        WsbAssert(0 != pPath, E_POINTER);
        WsbAffirmHr(m_path.CopyTo(pPath, bufferSize));

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetPathForFind(
    IN OLECHAR* searchName,
    OUT OLECHAR** pPath,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaScanItem::GetPathForFind().

--*/
{
    HRESULT          hr = S_OK;
    CWsbStringPtr    tmpString;

    try {

        WsbAssert(0 != pPath, E_POINTER);

        // Get a buffer.
        WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize));

        try {

            // Get the path to the resource of the resource.
            //
            WsbAffirmHr(m_pResource->GetPath(&tmpString, 0));
            WsbAffirmHr(tmpString.Prepend(OLESTR("\\\\?\\")));
            //WsbAffirmHr(tmpString.Append(OLESTR("\\")));

            // Copy in the path.
            //WsbAffirmHr(tmpString.Prepend(OLESTR("\\\\?\\")));
            WsbAffirmHr(tmpString.Append(&(m_path[1])));
            WsbAffirmHr(tmpString.Append(searchName));

        } WsbCatch(hr);

        WsbAffirmHr(tmpString.GiveTo(pPath));

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetPathAndName(
    IN    OLECHAR* appendix,
    OUT OLECHAR** pPath,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaScanItem::GetPathAndName().

--*/
{
    HRESULT          hr = S_OK;
    CWsbStringPtr    tmpString;

    try {

        WsbAssert(0 != pPath, E_POINTER);

        // Get a buffer.
        WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize));

        try {

            tmpString = m_path;
            tmpString.Append(m_findData.cFileName);

            if (0 != appendix) {
                tmpString.Append(appendix);
            }

        } WsbCatch(hr);

        // Give responsibility for freeing the memory back to the caller.
        WsbAffirmHr(tmpString.GiveTo(pPath));

    } WsbCatch(hr);


    return(hr);
}


HRESULT
CFsaScanItem::GetFullPathAndName(
    IN    OLECHAR* prependix,
    IN    OLECHAR* appendix,
    OUT OLECHAR** pPath,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaScanItem::GetFullPathAndName().

--*/
{
    HRESULT          hr = S_OK;
    CWsbStringPtr    tmpString;
    CWsbStringPtr    tmpString2;

    try {

        WsbAssert(0 != pPath, E_POINTER);

        // Get a buffer.
        WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize));

        try {
            if (0 != prependix) {
                tmpString = prependix;
                // Get the path to the resource of the resource.
                WsbAffirmHr(m_pResource->GetPath(&tmpString2, 0));
                WsbAffirmHr(tmpString.Append(tmpString2));
            } else {
                WsbAffirmHr(m_pResource->GetPath(&tmpString, 0));
            }

            // Copy in the path.
            WsbAffirmHr(tmpString.Append(&(m_path[1])));
            WsbAffirmHr(tmpString.Append(m_findData.cFileName));
            if (0 != appendix) {
                WsbAffirmHr(tmpString.Append(appendix));
            }

        } WsbCatch(hr);

        // Give responsibility for freeing the memory back to the caller.
        WsbAffirmHr(tmpString.GiveTo(pPath));


    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetPhysicalSize(
    OUT LONGLONG* pSize
    )

/*++

Implements:

  IFsaScanItem::GetPhysicalSize().

--*/
{
    HRESULT          hr = S_OK;
    CWsbStringPtr    path;

    try {

        WsbAssert(0 != pSize, E_POINTER);

        //WsbAssertHr(GetFullPathAndName(NULL, 0, &path, 0));
        WsbAssertHr(GetFullPathAndName(OLESTR("\\\\?\\"), 0, &path, 0));

        // Only read this value in once, but wait until it is asked for
        // before reading it in (since this call takes time and many scans
        // won't need the information.
        if (!m_gotPhysicalSize) {
            m_physicalSize.LowPart = GetCompressedFileSize(path, &m_physicalSize.HighPart);
            if (MAXULONG == m_physicalSize.LowPart) {
                //  Have to check last error since  MAXULONG could be a valid
                //  value for the low part of the size.
                DWORD err = GetLastError();

                if (err != NO_ERROR) {
                    WsbTrace(OLESTR("CFsaScanItem::GetPhysicalSize of %ws Last error = %u\n"),
                        (WCHAR *) path, err);
                }

                WsbAffirm(NO_ERROR == err, E_FAIL);
            }
            m_gotPhysicalSize = TRUE;
        }

        *pSize = m_physicalSize.QuadPart;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetPremigratedUsn(
    OUT LONGLONG* pFileUsn
    )

/*++

Implements:

Routine Description:

    Get the USN Journal number for this file from the premigrated list.

Arguments:

    pFileUsn - Pointer to File USN to be returned.

Return Value:

    S_OK   - success

--*/
{
    HRESULT            hr = S_OK;

    try {
        CComPtr<IWsbDbSession>              pDbSession;
        CComPtr<IFsaPremigratedDb>          pPremDb;
        CComPtr<IFsaResourcePriv>            pResourcePriv;

        WsbAssert(pFileUsn, E_POINTER);

        //  Get the premigrated list DB
        WsbAffirmHr(m_pResource->QueryInterface(IID_IFsaResourcePriv,
                (void**) &pResourcePriv));
        WsbAffirmHr(pResourcePriv->GetPremigrated(IID_IFsaPremigratedDb,
                (void**) &pPremDb));

        //  Open the premigration list
        WsbAffirmHr(pPremDb->Open(&pDbSession));

        try {
            FSA_PLACEHOLDER                     PlaceHolder;
            CComPtr<IFsaPremigratedRec>         pPremRec;
            LONGLONG                            usn;

            //  Get a DB entity for the search
            WsbAffirmHr(pPremDb->GetEntity(pDbSession, PREMIGRATED_REC_TYPE,
                    IID_IFsaPremigratedRec, (void**) &pPremRec));
            WsbAffirmHr(pPremRec->UseKey(PREMIGRATED_BAGID_OFFSETS_KEY_TYPE));

            //  Find the record
            WsbAffirmHr(GetPlaceholder(0, 0, &PlaceHolder));
            WsbAffirmHr(pPremRec->SetBagId(PlaceHolder.bagId));
            WsbAffirmHr(pPremRec->SetBagOffset(PlaceHolder.fileStart));
            WsbAffirmHr(pPremRec->SetOffset(PlaceHolder.dataStreamStart));
            WsbAffirmHr(pPremRec->FindEQ());

            //  Get the stored USN
            WsbAffirmHr(pPremRec->GetFileUSN(&usn));
            *pFileUsn = usn;
        } WsbCatch(hr);

        //  Close the DB
        pPremDb->Close(pDbSession);

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetSession(
    OUT IHsmSession** ppSession
    )

/*++

Implements:

  IFsaScanItem::GetSession().

--*/
{
    HRESULT            hr = S_OK;

    try {

        WsbAssert(0 != ppSession, E_POINTER);

        *ppSession = m_pSession;
        m_pSession->AddRef();

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::GetUncPathAndName(
    IN    OLECHAR* prependix,
    IN    OLECHAR* appendix,
    OUT OLECHAR** pPath,
    IN ULONG bufferSize
    )

/*++

Implements:

  IFsaScanItem::GetUncPathAndName().

--*/
{
    HRESULT          hr = S_OK;
    CWsbStringPtr    tmpString;
    CWsbStringPtr    tmpString2;

    try {

        WsbAssert(0 != pPath, E_POINTER);

        // Get a buffer.
        WsbAffirmHr(tmpString.TakeFrom(*pPath, bufferSize));

        try {
            if (0 != prependix) {
                tmpString = prependix;
                // Get the path to the resource of the resource.
                WsbAffirmHr(m_pResource->GetUncPath(&tmpString2, 0));
                WsbAffirmHr(tmpString.Append(tmpString2));
            } else {
                WsbAffirmHr(m_pResource->GetPath(&tmpString, 0));
            }

            // Copy in the path.
            WsbAffirmHr(tmpString.Append(&(m_path[1])));
            WsbAffirmHr(tmpString.Append(m_findData.cFileName));
            if (0 != appendix) {
                WsbAffirmHr(tmpString.Append(appendix));
            }

        } WsbCatch(hr);

        // Give responsibility for freeing the memory back to the caller.
        WsbAffirmHr(tmpString.GiveTo(pPath));


    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::IsAParent(
    void
    )

/*++

Implements:

  IFsaScanItem::IsAParent().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsARelativeParent(
    void
    )

/*++

Implements:

  IFsaScanItem::IsARelativeParent().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {

        // looking for "."
        if (m_findData.cFileName[0] == L'.') {

            if (m_findData.cFileName[1] == 0) {
                hr = S_OK;
            }

            // looking for "."
            else if (m_findData.cFileName[1] == L'.') {

                if (m_findData.cFileName[2] == 0) {
                    hr = S_OK;
                }
            }
        }
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsCompressed(
    void
    )

/*++

Implements:

  IFsaScanItem::IsCompressed().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsEncrypted(
    void
    )

/*++

Implements:

  IFsaScanItem::IsEncrypted().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsDeleteOK(
    IN IFsaPostIt *pPostIt
    )

/*++

Implements:

  IFsaScanItem::IsDeleteOK().

--*/
{
    HRESULT            hr = S_OK;
    WsbTraceIn(OLESTR("CFsaScanItem::IsDeleteOK"), OLESTR(""));

    try  {
        //
        // Get the version ID from the FSA Post it.  This is the
        // version of the file at the time of the migrate request
        //
        LONGLONG            workVersionId;
        WsbAffirmHr(pPostIt->GetFileVersionId(&workVersionId));

        //
        // Get the version of the file at the time of this scan
        //
        LONGLONG            scanVersionId;
        WsbAffirmHr(GetVersionId(&scanVersionId));

        //
        // See if the versions match
        //
        WsbTrace(OLESTR("CFsaScanItem::IsDeleteOK: workVersionId:<%I64u> scanVersionId:<%I64u>\n"),
            workVersionId, scanVersionId);

        if (workVersionId != scanVersionId)  {
            WsbTrace(OLESTR("CFsaScanItem::IsDeleteOK: File version has changed!\n"));
            WsbThrow(FSA_E_FILE_CHANGED);
        }

    } WsbCatch(hr);


    WsbTraceOut(OLESTR("CFsaScanItem::IsDeleteOk"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}

HRESULT
CFsaScanItem::IsGroupMemberOf(
    OLECHAR* /*group*/
    )

/*++

Implements:

  IFsaScanItem::IsGroupMemberOf().

--*/
{
    HRESULT            hr = S_FALSE;

    hr = E_NOTIMPL;

    return(hr);
}


HRESULT
CFsaScanItem::IsHidden(
    void
    )

/*++

Implements:

  IFsaScanItem::IsHidden().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsManageable(
    IN LONGLONG offset,
    IN LONGLONG size
    )

/*++

Implements:

  IFsaScanItem::IsManageable().

--*/
{
    HRESULT         hr = S_FALSE;
    HRESULT         hr2;
    LONGLONG        logicalSize;
    LONGLONG        managableSize;
    FILETIME        time;
    FILETIME        managableTime;
    BOOL            isRelative;

    //
    // Get some strings for logging and tracing
    //
    CWsbStringPtr    fileName;
    CWsbStringPtr    jobName;
    try  {
        WsbAffirmHr(GetFullPathAndName( 0, 0, &fileName, 0));
        WsbAffirmHr(m_pSession->GetName(&jobName, 0));
    } WsbCatch( hr );

    WsbTraceIn(OLESTR("CFsaScanItem::IsManageable"), OLESTR("<%ls>"), (OLECHAR *)fileName);
    try {

        // To be managable the item:
        //    - can't already be managed (premigratted or truncated)
        //  - can't be a link
        //  - can't be encrypted
        //  - can't be sparse
        //  - can't have extended attributes (reparse point limitation)
        //  - must have a size bigger than the resource's default size
        //  - must have a last access time older than the resource's default time

        // Managed?
        hr2 = IsManaged(offset, size);
        if (S_FALSE == hr2) {

            // A link?
            hr2 = IsALink();
            if (S_FALSE == hr2) {

                // Encrypted?
                hr2 = IsEncrypted();
                if (S_FALSE == hr2) {

                    // A sparse?
                    hr2 = IsSparse();
                    if (S_FALSE == hr2) {

                        // A sparse?
                        hr2 = HasExtendedAttributes();
                        if (S_FALSE == hr2) {

                            // Big enough?
                            WsbAffirmHr(GetLogicalSize(&logicalSize));
                            WsbAffirmHr(m_pResource->GetManageableItemLogicalSize(&managableSize));
                            if (logicalSize >= managableSize) {

                                // Old enough?
                                WsbAffirmHr(GetAccessTime(&time));
                                WsbAffirmHr(m_pResource->GetManageableItemAccessTime(&isRelative, &managableTime));
                                if (WsbCompareFileTimes(time, managableTime, isRelative, FALSE) >= 0) {

                                    // It can be managed!!
                                    hr = S_OK;
                                }  else  {
                                    WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISACCESSED, 0, NULL,  (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
                                }
                            } else  {
                                WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISTOOSMALL, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
                                WsbTrace( OLESTR("LogicalSize is %I64d; ManagableSize is %I64d\n"), logicalSize, managableSize);
                            }
                        } else  {
                            WsbLogEvent(FSA_MESSAGE_FILESKIPPED_HASEA, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
                        }
                    } else  {
                        WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISSPARSE, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
                    }
                } else  {
                       WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISENCRYPTED, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
                }
            } else  {
                WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISALINK, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
            }
        } else  {
            WsbLogEvent(FSA_MESSAGE_FILESKIPPED_ISMANAGED, 0, NULL, (OLECHAR*) jobName, WsbAbbreviatePath(fileName, 120), WsbHrAsString(hr), NULL);
        }


    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::IsManageable"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}

HRESULT
CFsaScanItem::IsMigrateOK(
    IN IFsaPostIt *pPostIt
    )

/*++

Implements:

  IFsaScanItem::IsMigrateOK().

--*/
{
    HRESULT            hr = S_OK;
    WsbTraceIn(OLESTR("CFsaScanItem::IsMigrateOK"), OLESTR(""));

    try  {
        //
        // Make sure the file isn't already managed.  This could happen if two jobs were scanning
        // the same volume.
        //
        LONGLONG                    offset;
        LONGLONG                    size;

        WsbAffirmHr(pPostIt->GetRequestOffset(&offset));
        WsbAffirmHr(pPostIt->GetRequestSize(&size));
        if (IsManaged(offset, size) == S_OK)  {
            //
            // The file is already managed so skip it
            //
            WsbTrace(OLESTR("A manage request for an already managed file - skip it!\n"));
            WsbThrow(FSA_E_FILE_ALREADY_MANAGED);
        }

        //
        // Get the version ID from the FSA Post it.  This is the
        // version of the file at the time of the migrate request
        //
        LONGLONG            workVersionId;
        WsbAffirmHr(pPostIt->GetFileVersionId(&workVersionId));

        //
        // Get the version of the file at the time of this scan
        //
        LONGLONG            scanVersionId;
        WsbAffirmHr(GetVersionId(&scanVersionId));

        //
        // See if the versions match
        //
        WsbTrace(OLESTR("CFsaScanItem::IsMigrateOK: workVersionId:<%I64u> scanVersionId:<%I64u>\n"),
            workVersionId, scanVersionId);

        if (workVersionId != scanVersionId)  {
            WsbTrace(OLESTR("CFsaScanItem::IsMigrateOK: File version has changed!\n"));
            WsbThrow(FSA_E_FILE_CHANGED);
        }


    } WsbCatch(hr);


    WsbTraceOut(OLESTR("CFsaScanItem::IsMigrateOK"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}


HRESULT
CFsaScanItem::IsMbit(
    void
    )

/*++

Implements:

  IFsaScanItem::IsMbit().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsOffline(
    void
    )
/*++

Implements:

    IFsaScanItem::IsOffline().

--*/
{
    HRESULT             hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsOwnerMemberOf(
    OLECHAR* /*group*/
    )

/*++

Implements:

  IFsaScanItem::IsOwnerMemberOf().

--*/
{
    HRESULT            hr = S_FALSE;

    hr = E_NOTIMPL;

    return(hr);
}


HRESULT
CFsaScanItem::IsReadOnly(
    void
    )

/*++

Implements:

  IFsaScanItem::IsReadOnly().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::IsRecallOK(
    IN IFsaPostIt *pPostIt
    )

/*++

Implements:

  IFsaScanItem::IsRecallOK().

--*/
{
    HRESULT            hr = S_OK;
    WsbTraceIn(OLESTR("CFsaScanItem::IsRecallOK"), OLESTR(""));

    try  {
        LONGLONG offset;
        LONGLONG size;
        //
        // Make sure the file is still truncated
        //
        WsbAffirmHr(pPostIt->GetRequestOffset(&offset));
        WsbAffirmHr(pPostIt->GetRequestSize(&size));
        hr = IsTruncated(offset, size);
        if (S_OK != hr)  {
            //
            // The file is not truncated, so skip it
            //
            WsbTrace(OLESTR("CFsaScanItem::IsRecallOK - file isn't truncated.\n"));
            WsbThrow(FSA_E_FILE_NOT_TRUNCATED);
        }

        // Get the version ID from the FSA Post it.  This is the
        // version of the file at the time of the migrate request
        //
        LONGLONG            workVersionId;
        WsbAffirmHr(pPostIt->GetFileVersionId(&workVersionId));

        //
        // Get the version of the file
        //
        LONGLONG            scanVersionId;
        WsbAffirmHr(GetVersionId(&scanVersionId));

        //
        // See if the versions match
        //
        WsbTrace(OLESTR("CFsaScanItem::IsRecallOK: workVersionId:<%I64u> scanVersionId:<%I64u>\n"),
            workVersionId, scanVersionId);

        if (workVersionId != scanVersionId)  {
            WsbTrace(OLESTR("CFsaScanItem::IsRecallOK: File version has changed!\n"));

            //
            // If the use has changed alternate data streams
            // the file version ID may have changed but it is
            // OK to recall the file.  So if the version ID's
            // don't match, then check to see if the truncated
            // part of the file is OK.  If so, allow the recall
            // to happen.
            //

            //
            // Check to see if the whole file is still sparse
            //
            if (IsTotallySparse() == S_OK)  {
                //
                // The file is OK so far to recall but we need
                // to make the last modify dates match
                //
                FSA_PLACEHOLDER     placeholder;
                WsbAffirmHr(pPostIt->GetPlaceholder(&placeholder));;
                placeholder.fileVersionId = scanVersionId;
                WsbAffirmHr(pPostIt->SetPlaceholder(&placeholder));
            } else  {
                //
                // The file has been changed, recalling data will
                // overwrite something that has been added since the
                // truncation occurred.  So don't do anything.
                //
                WsbTrace(OLESTR("File is no longer sparse.!\n"));
                WsbThrow(FSA_E_FILE_CHANGED);
            }


        }

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::IsRecallOK"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}


HRESULT
CFsaScanItem::IsSparse(
    void
    )

/*++

Implements:

  IFsaScanItem::IsSparse().

--*/
{
    HRESULT         hr = S_FALSE;
    LONGLONG        size;

    WsbTraceIn(OLESTR("CFsaScanItem::IsSparse"), OLESTR(""));
       
    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0) {
        hr = GetLogicalSize( &size ) ;
        if ( S_OK == hr ) {
            hr = CheckIfSparse(0, size );
            if ( (FSA_E_FILE_IS_TOTALLY_SPARSE == hr) ||
                 (FSA_E_FILE_IS_PARTIALLY_SPARSE == hr) ) {
                hr = S_OK;
            } else {
                hr = S_FALSE;
            }
        }
    }
    WsbTraceOut(OLESTR("CFsaScanItem::IsSparse"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}


HRESULT
CFsaScanItem::IsTotallySparse(
    void
    )

/*++

Implements:

  IFsaScanItem::IsTotallySparse().

--*/
{
    HRESULT         hr = S_FALSE;
    LONGLONG        size;

    WsbTraceIn(OLESTR("CFsaScanItem::IsTotallySparse"), OLESTR(""));
    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0) {
        hr = GetLogicalSize( &size ) ;
        if ( S_OK == hr ) {
            hr = CheckIfSparse(0, size );
            if (FSA_E_FILE_IS_TOTALLY_SPARSE == hr)  {
                    hr = S_OK;
            } else  {
                hr = S_FALSE;
            }
        }
    }

    WsbTraceOut(OLESTR("CFsaScanItem::IsTotallySparse"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}

HRESULT
CFsaScanItem::Manage(
    IN LONGLONG offset,
    IN LONGLONG size,
    IN GUID storagePoolId,
    IN BOOL truncate
    )

/*++

Implements:

  IFsaScanItem::Manage().

--*/
{
    HRESULT            hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::Manage"), OLESTR(""));

    try {

        WsbAssert(GUID_NULL != storagePoolId, E_INVALIDARG);
        WsbAffirmHr(m_pResource->Manage((IFsaScanItem*) this, offset, size, storagePoolId, truncate));

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::Manage"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaScanItem::Move(
    OLECHAR* dest,
    BOOL /*retainHierarcy*/,
    BOOL /*expandPlaceholders*/,
    BOOL overwriteExisting
    )

/*++

Implements:

  IFsaScanItem::Move().

--*/
{
    HRESULT          hr = S_OK;
    DWORD            mode = MOVEFILE_COPY_ALLOWED;

    try {

        // NOTE : This default behavior causes placeholders
        // to be expanded when moving to another volume and probably doesn't
        // retain the heirarchy.
        WsbAssert(0 != dest, E_POINTER);

        if (overwriteExisting) {
            mode |= MOVEFILE_REPLACE_EXISTING;
        }

        WsbAssert(MoveFileEx(m_findData.cFileName, dest, mode), E_FAIL);

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::Recall(
    IN LONGLONG offset,
    IN LONGLONG size,
    IN BOOL deletePlaceholder
    )

/*++

Implements:

  IFsaScanItem::Recall().

--*/
{
    HRESULT            hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::Recall"), OLESTR(""));

    try {

        WsbAffirmHr(m_pResource->Recall((IFsaScanItem*) this, offset, size, deletePlaceholder));

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::Recall"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaScanItem::Recycle(
    void
    )

/*++

Implements:

  IFsaScanItem::Recycle().

--*/
{
    HRESULT            hr = S_OK;

    try {

        // Probably need to look at SHFileOperation().

        hr = E_NOTIMPL;

    } WsbCatch(hr);

    return(hr);
}


HRESULT
CFsaScanItem::IsSystem(
    void
    )

/*++

Implements:

  IFsaScanItem::IsSystem().

--*/
{
    HRESULT            hr = S_FALSE;

    if ((m_findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0) {
        hr = S_OK;
    }

    return(hr);
}


HRESULT
CFsaScanItem::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
CFsaScanItem::Unmanage(
    IN LONGLONG offset,
    IN LONGLONG size
    )

/*++

Implements:

  IFsaScanItem::Unmanage().

--*/
{
    HRESULT            hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::Unmanage"), OLESTR("<%ls>"),
            WsbAbbreviatePath(m_path, 120));

    try {

        // We only need to worry about files that have placeholder information.
        if (IsManaged(offset, size) == S_OK) {

            // If the file is truncated, then we need to recall the data
            // before deleting the placeholder information.
            // NOTE: We set a flag on the Recall so the placeholder will
            // be deleted after the file is recalled.
            if (IsTruncated(offset, size) == S_OK) {
                WsbAffirmHr(Recall(offset, size, TRUE));
            } else {

                //  For disaster recovery, it would be better to delete the placeholder
                //  and THEN remove this file from the premigration list.  Unfortunately,
                //  after deleting the placeholder, the RemovePremigrated call fails
                //  because it needs to get some information from the placeholder (which
                //  is gone).  So we do it in this order.
                hr = m_pResource->RemovePremigrated((IFsaScanItem*) this, offset, size);
                if (WSB_E_NOTFOUND == hr) {
                    //  It's no tragedy if this file wasn't in the list since we were
                    //  going to delete it anyway (although it shouldn't happen) so
                    //  let's continue anyway
                    hr = S_OK;
                }
                WsbAffirmHr(hr);
                WsbAffirmHr(DeletePlaceholder(offset, size));
            }
        }

    } WsbCatch(hr);
    WsbTraceOut(OLESTR("CFsaScanItem::Unmanage"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}


HRESULT
CFsaScanItem::Validate(
    IN LONGLONG offset,
    IN LONGLONG size
    )

/*++

Implements:

  IFsaScanItem::Validate().

--*/
{
    HRESULT         hr = S_OK;
    BOOL            fileIsTruncated = FALSE;
    LONGLONG        usn = 0;

    WsbTraceIn(OLESTR("CFsaScanItem::Validate"), OLESTR("offset = <%I64u>, size = <%I64u>"),
            offset, size);
    try {
        //
        // Do some local validation before calling the engine.
        //

        // We only need to worry about files that have placeholder information.
        if (IsManaged(offset, size) == S_OK) {
            //
            // If the file is marked as truncated, make sure it is still truncated.
            //
            if (IsTruncated(offset, size) == S_OK) {
                //
                // Check to see if the file is totally sparse to see if it is truncated.
                //
                if (IsTotallySparse() != S_OK)  {
                    //
                    // The file is marked as truncated but is not truncated
                    // Make it truncated.
                    //
                    WsbAffirmHr(Truncate(offset,size));
                    WsbLogEvent(FSA_MESSAGE_VALIDATE_TRUNCATED_FILE, 0, NULL,  WsbAbbreviatePath(m_path, 120), WsbHrAsString(hr), NULL);
                }
            fileIsTruncated = TRUE;
            }
        }

        //
        // The last modify date may be updated on a file if the named data streams
        // have been modified.  So check to see if the dates match.  If they don't,
        // if the file is trunctated, see if it is still truncated, if so, update the
        // modify date in the placeholder to the file's modify date.  If the file is
        // premigrated and the modify dates don't match, delete the placeholder.

        // Get the version ID from the file
        LONGLONG            scanVersionId;
        WsbAffirmHr(GetVersionId(&scanVersionId));

        // Get the version ID from the placeholder
        FSA_PLACEHOLDER     scanPlaceholder;
        WsbAffirmHr(GetPlaceholder(offset, size, &scanPlaceholder));

        if (TRUE == fileIsTruncated)  {

            // Check to see if the dates match
            if (scanPlaceholder.fileVersionId != scanVersionId)  {
                WsbTrace(OLESTR("CFsaScanItem::Validate - placeholer version ID = <%I64u>, file version Id = <%I64u>"),
                        scanPlaceholder.fileVersionId, scanVersionId);
                //
                // Update the placeholder information on the reparse point
                //
                LONGLONG afterPhUsn;
                scanPlaceholder.fileVersionId = scanVersionId;
                WsbAffirmHr(CreatePlaceholder(offset, size, scanPlaceholder, FALSE, 0, &afterPhUsn));
                WsbLogEvent(FSA_MESSAGE_VALIDATE_RESET_PH_MODIFY_TIME, 0, NULL,  WsbAbbreviatePath(m_path, 120), WsbHrAsString(hr), NULL);
            }
        } else {
            // The file is pre-migrated.  Verify that it has not changed since we managed it and if it has then unmanage it.
            if (Verify(offset, size) != S_OK) {
                WsbAffirmHr(Unmanage(offset, size));
                WsbLogEvent(FSA_MESSAGE_VALIDATE_UNMANAGED_FILE, 0, NULL,  WsbAbbreviatePath(m_path, 120), WsbHrAsString(hr), NULL);
            }
        }

        // Now that all of this stuff is OK, call the engine
        if (IsManaged(offset, size) == S_OK) {
            WsbAffirmHr(m_pResource->Validate((IFsaScanItem*) this, offset, size, usn));
        }

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::Validate"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
    return(hr);
}

HRESULT
CFsaScanItem::FindFirstInDbIndex(
    IN IFsaResource* pResource,
    IN IHsmSession* pSession
    )

/*++

Implements:

  IFsaScanItemPriv::FindFirstInDbIndex().

--*/
{
    HRESULT                  hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::FindFirstInDbIndex"), OLESTR(""));

    try {
        CComPtr<IFsaResourcePriv>   pResourcePriv;

        WsbAssert(0 != pResource, E_POINTER);

        // Store off some of the scan information.
        m_pResource = pResource;
        m_pSession = pSession;

        // If Db is already present (could happen if somebody calls First() twice in a row),
        // we close the Db and reopen since we cannot be sure that the resource is the same!
        if (m_pUnmanageDb != NULL) {
            // Db must be open
            (void)m_pUnmanageDb->Close(m_pDbSession);
            m_pDbSession = 0;
            m_pUnmanageRec = 0;
            m_pUnmanageDb = 0;
        }

        // Get and open the Unmanage db 
        // (Note: if this scanning is ever extended to use another DB, 
        // this method should get additional parameter for which DB to scan)
        WsbAffirmHr(m_pResource->QueryInterface(IID_IFsaResourcePriv,
                (void**) &pResourcePriv));
        hr = pResourcePriv->GetUnmanageDb(IID_IFsaUnmanageDb,
                (void**) &m_pUnmanageDb);
        if (WSB_E_RESOURCE_UNAVAILABLE == hr) {
            // Db was not created ==> no files to scan
            hr = WSB_E_NOTFOUND;
        }
        WsbAffirmHr(hr);

        hr = m_pUnmanageDb->Open(&m_pDbSession);
        if (S_OK != hr) {
            m_pUnmanageDb = NULL;
            WsbAffirmHr(hr);
        }

        // Get a record to traverse with and set for sequential traversing
        WsbAffirmHr(m_pUnmanageDb->GetEntity(m_pDbSession, UNMANAGE_REC_TYPE, IID_IFsaUnmanageRec,
                (void**)&m_pUnmanageRec));
        WsbAffirmHr(m_pUnmanageRec->SetSequentialScan());

        //  Get file information
        WsbAffirmHr(GetFromDbIndex(TRUE));

    } WsbCatch(hr);

    WsbTraceOut(OLESTR("CFsaScanItem::FindFirstInDbIndex"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}

HRESULT
CFsaScanItem::FindNextInDbIndex(
    void
    )

/*++

Implements:

  IFsaScanItemPriv::FindNextInDbIndex().

--*/
{
    HRESULT                 hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::FindNextInDbIndex"), OLESTR(""));

    try {
        WsbAssert(m_pUnmanageDb != NULL, E_FAIL);

        //  Get file information
        WsbAffirmHr(GetFromDbIndex(FALSE));

    } WsbCatch(hr);
    
    WsbTraceOut(OLESTR("CFsaScanItem::FindNextInDbIndex"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}

HRESULT
CFsaScanItem::GetFromDbIndex(
    BOOL first
    )

/*

Implements:

  CFsaScanItem::GetFromDbIndex().

--*/
{
    HRESULT                 hr = S_OK;

    WsbTraceIn(OLESTR("CFsaScanItem::GetFromDbIndex"), OLESTR(""));

    try {
        IFsaScanItem*   pScanItem;
        HRESULT         hrFindFileId = S_OK;
        LONGLONG        fileId;
        BOOL            bCont;

        WsbAssert(m_pUnmanageDb != NULL, E_FAIL);
        WsbAssert(m_pUnmanageRec != NULL, E_FAIL);

        do {
            bCont = FALSE;

            // Get first/next record
            if (first) {
                hr = m_pUnmanageRec->First();
            } else {
                hr = m_pUnmanageRec->Next();
            }
            WsbAffirm(S_OK == hr, WSB_E_NOTFOUND);

            // Get file id
            WsbAffirmHr(m_pUnmanageRec->GetFileId(&fileId));
   
            //  Reset some items in case this isn't the first call to FindFileId 
            //  (FindFileId actually "attach" the object to a different file)
            if (INVALID_HANDLE_VALUE != m_handle) {
                FindClose(m_handle);
                m_handle = INVALID_HANDLE_VALUE;
            }
            if (TRUE == m_changedAttributes) {
                RestoreAttributes();
            }

            //  Find the file from the ID 
            pScanItem = this;
            hrFindFileId = m_pResource->FindFileId(fileId, m_pSession, &pScanItem);

            //  If the FindFileId failed, we just skip that item and get the 
            //  next one.  This is to keep the scan from just stopping on this
            //  item.  FindFileId could fail because the file has been deleted
            //  or open exclusively by somebody else
            if (!SUCCEEDED(hrFindFileId)) {
                WsbTrace(OLESTR("CFsaScanItem::GetFromDbIndex: file id %I64d skipped since FindFileId failed with hr = <%ls>\n"),
                    fileId, WsbHrAsString(hrFindFileId));
                first = FALSE;
                bCont = TRUE;
            } 
        } while (bCont);

        WsbAffirmHr(pScanItem->Release());  // Get rid of extra ref. count (we get extra ref count only when FindFileId succeeds)

    } WsbCatch(hr);
    
    WsbTraceOut(OLESTR("CFsaScanItem::GetFromDbIndex"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));

    return(hr);
}